@hyperscale0/hsx 2.4.0 → 3.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 (249) hide show
  1. package/CHANGELOG.md +2 -400
  2. package/README.md +3 -92
  3. package/dist/src/ast.d.ts +99 -310
  4. package/dist/src/ast.d.ts.map +1 -1
  5. package/dist/src/ast.js +16 -69
  6. package/dist/src/ast.js.map +1 -1
  7. package/dist/src/cli.d.ts +6 -19
  8. package/dist/src/cli.d.ts.map +1 -1
  9. package/dist/src/cli.js +76 -315
  10. package/dist/src/cli.js.map +1 -1
  11. package/dist/src/compile.d.ts +23 -73
  12. package/dist/src/compile.d.ts.map +1 -1
  13. package/dist/src/compile.js +1113 -148
  14. package/dist/src/compile.js.map +1 -1
  15. package/dist/src/cost.d.ts +8 -77
  16. package/dist/src/cost.d.ts.map +1 -1
  17. package/dist/src/cost.js +13 -422
  18. package/dist/src/cost.js.map +1 -1
  19. package/dist/src/format.d.ts +8 -7
  20. package/dist/src/format.d.ts.map +1 -1
  21. package/dist/src/format.js +9 -175
  22. package/dist/src/format.js.map +1 -1
  23. package/dist/src/headers.d.ts +35 -0
  24. package/dist/src/headers.d.ts.map +1 -0
  25. package/dist/src/headers.js +108 -0
  26. package/dist/src/headers.js.map +1 -0
  27. package/dist/src/index.d.ts +6 -12
  28. package/dist/src/index.d.ts.map +1 -1
  29. package/dist/src/index.js +4 -9
  30. package/dist/src/index.js.map +1 -1
  31. package/dist/src/lex.d.ts +8 -28
  32. package/dist/src/lex.d.ts.map +1 -1
  33. package/dist/src/lex.js +88 -146
  34. package/dist/src/lex.js.map +1 -1
  35. package/dist/src/parse.d.ts +4 -17
  36. package/dist/src/parse.d.ts.map +1 -1
  37. package/dist/src/parse.js +402 -1040
  38. package/dist/src/parse.js.map +1 -1
  39. package/dist/src/std-bundle.d.ts.map +1 -1
  40. package/dist/src/std-bundle.js +12 -20
  41. package/dist/src/std-bundle.js.map +1 -1
  42. package/dist/src/std-library.d.ts +1 -1
  43. package/dist/src/std-library.d.ts.map +1 -1
  44. package/dist/src/std-library.js +1 -5
  45. package/dist/src/std-library.js.map +1 -1
  46. package/dist/src/tunables.d.ts +7 -0
  47. package/dist/src/tunables.d.ts.map +1 -0
  48. package/dist/src/tunables.js +26 -0
  49. package/dist/src/tunables.js.map +1 -0
  50. package/dist/src/version.d.ts +2 -4
  51. package/dist/src/version.d.ts.map +1 -1
  52. package/dist/src/version.js +2 -4
  53. package/dist/src/version.js.map +1 -1
  54. package/docs/README.md +179 -41
  55. package/docs/headers.md +44 -0
  56. package/examples/cost-table.json +99 -751
  57. package/examples/library.hsx +59 -0
  58. package/package.json +9 -17
  59. package/src/ast.ts +82 -441
  60. package/src/cli.ts +77 -362
  61. package/src/compile.ts +1535 -247
  62. package/src/cost.ts +28 -671
  63. package/src/format.ts +12 -256
  64. package/src/headers.ts +126 -0
  65. package/src/index.ts +7 -39
  66. package/src/lex.ts +99 -195
  67. package/src/parse.ts +403 -1209
  68. package/src/std-bundle.ts +12 -20
  69. package/src/std-library.ts +2 -7
  70. package/src/tunables.ts +31 -0
  71. package/src/version.ts +2 -5
  72. package/std/approvals.hsx +17 -0
  73. package/std/cards.hsx +63 -0
  74. package/std/collections.hsx +31 -0
  75. package/std/escrow.hsx +43 -0
  76. package/std/financing.hsx +372 -0
  77. package/std/insurance.hsx +59 -0
  78. package/std/lending.hsx +115 -0
  79. package/std/marketplace.hsx +24 -0
  80. package/std/money.hsx +195 -0
  81. package/std/savings.hsx +44 -0
  82. package/std/travel.hsx +98 -0
  83. package/std/wallet.hsx +41 -0
  84. package/dist/src/diagnostics.d.ts +0 -13
  85. package/dist/src/diagnostics.d.ts.map +0 -1
  86. package/dist/src/diagnostics.js +0 -587
  87. package/dist/src/diagnostics.js.map +0 -1
  88. package/dist/src/emit.d.ts +0 -51
  89. package/dist/src/emit.d.ts.map +0 -1
  90. package/dist/src/emit.js +0 -192
  91. package/dist/src/emit.js.map +0 -1
  92. package/dist/src/entry-overrides.d.ts +0 -58
  93. package/dist/src/entry-overrides.d.ts.map +0 -1
  94. package/dist/src/entry-overrides.js +0 -284
  95. package/dist/src/entry-overrides.js.map +0 -1
  96. package/dist/src/ir.d.ts +0 -73
  97. package/dist/src/ir.d.ts.map +0 -1
  98. package/dist/src/ir.js +0 -15
  99. package/dist/src/ir.js.map +0 -1
  100. package/dist/src/limits.d.ts +0 -23
  101. package/dist/src/limits.d.ts.map +0 -1
  102. package/dist/src/limits.js +0 -23
  103. package/dist/src/limits.js.map +0 -1
  104. package/dist/src/lsp/server.d.ts +0 -32
  105. package/dist/src/lsp/server.d.ts.map +0 -1
  106. package/dist/src/lsp/server.js +0 -391
  107. package/dist/src/lsp/server.js.map +0 -1
  108. package/dist/src/modules.d.ts +0 -38
  109. package/dist/src/modules.d.ts.map +0 -1
  110. package/dist/src/modules.js +0 -372
  111. package/dist/src/modules.js.map +0 -1
  112. package/dist/src/typecheck.d.ts +0 -8
  113. package/dist/src/typecheck.d.ts.map +0 -1
  114. package/dist/src/typecheck.js +0 -4131
  115. package/dist/src/typecheck.js.map +0 -1
  116. package/docs/guide/01-first-program.md +0 -24
  117. package/docs/guide/02-money.md +0 -25
  118. package/docs/guide/03-instruments.md +0 -93
  119. package/docs/guide/04-lifecycles.md +0 -40
  120. package/docs/guide/05-fees-and-splits.md +0 -45
  121. package/docs/guide/06-schedules.md +0 -26
  122. package/docs/guide/07-composition.md +0 -52
  123. package/docs/guide/08-writing-a-module.md +0 -85
  124. package/docs/guide/09-cost.md +0 -24
  125. package/docs/guide/10-diagnostics.md +0 -27
  126. package/docs/llms-full.txt +0 -3684
  127. package/docs/llms.txt +0 -44
  128. package/docs/piece-plans.md +0 -120
  129. package/docs/playground.md +0 -55
  130. package/docs/reference/cli.md +0 -36
  131. package/docs/reference/diagnostics.md +0 -901
  132. package/docs/reference/grammar.md +0 -38
  133. package/docs/reference/std/advance.md +0 -112
  134. package/docs/reference/std/cancellable_booking.md +0 -159
  135. package/docs/reference/std/captured_payment.md +0 -115
  136. package/docs/reference/std/conditional_disbursement.md +0 -101
  137. package/docs/reference/std/credit_facility.md +0 -108
  138. package/docs/reference/std/held_payment.md +0 -173
  139. package/docs/reference/std/instant_transfer.md +0 -97
  140. package/docs/reference/std/metered.md +0 -89
  141. package/docs/reference/std/pooled_split.md +0 -95
  142. package/docs/reference/std/premium_forward.md +0 -119
  143. package/docs/reference/std/reconciled_payout.md +0 -103
  144. package/docs/reference/std/rotating_pool.md +0 -130
  145. package/docs/reference/std/scheduled.md +0 -136
  146. package/docs/reference/std/security_deposit.md +0 -138
  147. package/docs/reference/std/settlement_batch.md +0 -123
  148. package/docs/reference/std/swap.md +0 -133
  149. package/docs/reference/std/threshold_pool.md +0 -138
  150. package/docs/reference/std/weighted_distribution.md +0 -135
  151. package/docs/reference/types.md +0 -61
  152. package/docs/reference/udl-output.md +0 -13
  153. package/examples/01-first-program/README.md +0 -45
  154. package/examples/01-first-program/tip-jar.hsx +0 -17
  155. package/examples/02-imports-and-modules/README.md +0 -96
  156. package/examples/02-imports-and-modules/photo-booth.hsx +0 -37
  157. package/examples/03-diagnostics/README.md +0 -68
  158. package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
  159. package/examples/03-diagnostics/corner-shop.hsx +0 -19
  160. package/examples/04-complete-product/README.md +0 -84
  161. package/examples/04-complete-product/study-hall.hsx +0 -67
  162. package/examples/05-authored-instrument/README.md +0 -5
  163. package/examples/05-authored-instrument/payment.hsx +0 -37
  164. package/examples/05-watch-club/README.md +0 -6
  165. package/examples/05-watch-club/watch-club.hsx +0 -123
  166. package/examples/README.md +0 -48
  167. package/examples/advance/README.md +0 -3
  168. package/examples/advance/advance.hsx +0 -13
  169. package/examples/advance/advance.udl +0 -367
  170. package/examples/cancellable_booking/README.md +0 -3
  171. package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
  172. package/examples/cancellable_booking/cancellable_booking.udl +0 -413
  173. package/examples/captured_payment/README.md +0 -3
  174. package/examples/captured_payment/captured_payment.hsx +0 -17
  175. package/examples/captured_payment/captured_payment.udl +0 -686
  176. package/examples/conditional_disbursement/README.md +0 -3
  177. package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -15
  178. package/examples/conditional_disbursement/conditional_disbursement.udl +0 -418
  179. package/examples/credit_facility/README.md +0 -3
  180. package/examples/credit_facility/credit_facility.hsx +0 -24
  181. package/examples/credit_facility/credit_facility.udl +0 -1396
  182. package/examples/held_payment/README.md +0 -3
  183. package/examples/held_payment/held_payment.hsx +0 -13
  184. package/examples/held_payment/held_payment.udl +0 -569
  185. package/examples/instant_transfer/README.md +0 -3
  186. package/examples/instant_transfer/instant_transfer.hsx +0 -16
  187. package/examples/instant_transfer/instant_transfer.udl +0 -376
  188. package/examples/metered/README.md +0 -3
  189. package/examples/metered/metered.hsx +0 -13
  190. package/examples/metered/metered.udl +0 -253
  191. package/examples/pooled_split/README.md +0 -3
  192. package/examples/pooled_split/pooled_split.hsx +0 -15
  193. package/examples/pooled_split/pooled_split.udl +0 -432
  194. package/examples/premium_forward/README.md +0 -3
  195. package/examples/premium_forward/premium_forward.hsx +0 -19
  196. package/examples/premium_forward/premium_forward.udl +0 -604
  197. package/examples/reconciled_payout/README.md +0 -3
  198. package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
  199. package/examples/reconciled_payout/reconciled_payout.udl +0 -312
  200. package/examples/rotating_pool/README.md +0 -3
  201. package/examples/rotating_pool/rotating_pool.hsx +0 -18
  202. package/examples/rotating_pool/rotating_pool.udl +0 -4370
  203. package/examples/scheduled/README.md +0 -3
  204. package/examples/scheduled/scheduled.hsx +0 -12
  205. package/examples/scheduled/scheduled.udl +0 -355
  206. package/examples/security_deposit/README.md +0 -3
  207. package/examples/security_deposit/security_deposit.hsx +0 -21
  208. package/examples/security_deposit/security_deposit.udl +0 -320
  209. package/examples/settlement_batch/README.md +0 -3
  210. package/examples/settlement_batch/settlement_batch.hsx +0 -19
  211. package/examples/settlement_batch/settlement_batch.udl +0 -757
  212. package/examples/swap/README.md +0 -3
  213. package/examples/swap/swap.hsx +0 -19
  214. package/examples/swap/swap.udl +0 -892
  215. package/examples/threshold_pool/README.md +0 -3
  216. package/examples/threshold_pool/threshold_pool.hsx +0 -16
  217. package/examples/threshold_pool/threshold_pool.udl +0 -680
  218. package/examples/weighted_distribution/README.md +0 -3
  219. package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
  220. package/examples/weighted_distribution/weighted_distribution.udl +0 -348
  221. package/skills/hsx/SKILL.md +0 -449
  222. package/src/diagnostics.ts +0 -620
  223. package/src/emit.ts +0 -282
  224. package/src/entry-overrides.ts +0 -428
  225. package/src/ir.ts +0 -113
  226. package/src/limits.ts +0 -22
  227. package/src/lsp/server.ts +0 -460
  228. package/src/modules.ts +0 -452
  229. package/src/typecheck.ts +0 -5381
  230. package/std/SEMANTICS.md +0 -44
  231. package/std/money_flows/advance.hsx +0 -235
  232. package/std/money_flows/cancellable_booking.hsx +0 -402
  233. package/std/money_flows/captured_payment.hsx +0 -220
  234. package/std/money_flows/conditional_disbursement.hsx +0 -226
  235. package/std/money_flows/credit_facility.hsx +0 -214
  236. package/std/money_flows/held_payment.hsx +0 -812
  237. package/std/money_flows/index.hsx +0 -4
  238. package/std/money_flows/instant_transfer.hsx +0 -231
  239. package/std/money_flows/metered.hsx +0 -104
  240. package/std/money_flows/pooled_split.hsx +0 -109
  241. package/std/money_flows/premium_forward.hsx +0 -307
  242. package/std/money_flows/reconciled_payout.hsx +0 -119
  243. package/std/money_flows/rotating_pool.hsx +0 -305
  244. package/std/money_flows/scheduled.hsx +0 -599
  245. package/std/money_flows/security_deposit.hsx +0 -248
  246. package/std/money_flows/settlement_batch.hsx +0 -173
  247. package/std/money_flows/swap.hsx +0 -1192
  248. package/std/money_flows/threshold_pool.hsx +0 -307
  249. package/std/money_flows/weighted_distribution.hsx +0 -237
@@ -1,307 +0,0 @@
1
- module std.money_flows.threshold_pool
2
-
3
- // All-or-nothing capital accumulation pool collecting commitments toward a target amount before a close deadline.
4
- //
5
- // ### Purpose
6
- // `threshold_pool` powers all-or-nothing crowdfunding, capital calls, collective purchasing, and consortium rounds.
7
- // Multiple contributors pledge commitments toward a monetary `target`. Commitments sit in escrow until the pool
8
- // reaches the target before `close_by` (activating and settling funds to the beneficiary), or fails (triggering full refunds).
9
- //
10
- // ### Selection guidance
11
- // - vs `rotating_pool`: `threshold_pool` is all-or-nothing fundraising where many contributors fund a single beneficiary.
12
- // `rotating_pool` is a peer savings circle where members contribute identically in each cycle and rotate who receives the pot.
13
- // - vs `weighted_distribution`: `threshold_pool` pools funds inward from many contributors to one beneficiary.
14
- // `weighted_distribution` pays funds outward from one source pool to many weighted recipients.
15
- //
16
- // ### Parameters
17
- // - `contributor`: The party representing individual contributors.
18
- // - `beneficiary`: The beneficiary party receiving the settled pool if the target is met.
19
- // - `target`: Total target funding threshold in minor units of currency `C`.
20
- // - `commitment`: Binding name for individual contributor commitment amounts.
21
- // - `max_contributors`: Optional maximum count of admitted contributors.
22
- // - `close_by`: Stored date deadline by which the target must be met.
23
- // - `close_policy`: Policy determining pool close behavior (`threshold`).
24
- // - `overfund_policy`: Policy on commitments exceeding the target (`reject`).
25
- // - `cancel_policy`: Contributor withdrawal policy before close (`before_close`).
26
- // - `fail_policy`: Refund policy if the pool fails to hit target (`whole_commitment_refund`).
27
- // - `beneficiary_account`: Optional direct account reference for the beneficiary.
28
- // - `memo`: Optional memo text stored on the pool.
29
- // - `contribution_instrument`: Optional custom contribution child instrument block.
30
- // - `wording`: Optional custom UI wording block.
31
- //
32
- // ### Decision ports
33
- // None. Lifecycle transitions (`activate`, `fail`, `close`) are driven by target threshold evaluation and the `close_by` date.
34
- //
35
- // ### Example
36
- // ```hsx
37
- // program capital_pool_example "Capital pool example"
38
- // import { threshold_pool } from "std/money_flows"
39
- // party contributor: person
40
- // party company: business
41
- // settlement round = threshold_pool {
42
- // contributor: contributor
43
- // beneficiary: company
44
- // target: targetAmount: money(SAR)
45
- // commitment: commitmentAmount: money(SAR)
46
- // max_contributors: 100
47
- // close_by: closeBy
48
- // close_policy: threshold
49
- // overfund_policy: reject
50
- // cancel_policy: before_close
51
- // fail_policy: whole_commitment_refund
52
- // }
53
- // ```
54
- // ### Funding join
55
- // funding_join configures the flat contribution-instrument mode for a priced
56
- // obligation. Opening freezes minimum ticket, investor cap, profit and schedule
57
- // terms. Closing binds the obligation, freezes committed ticket identities and
58
- // moves the principal once into its funding account. A ticket's engine-owned
59
- // collection action has no independent payment or clock trigger.
60
- //
61
- export instrument threshold_pool<C>(
62
- contributor: optional<party>,
63
- beneficiary: optional<party>,
64
- target: money<C>,
65
- commitment: money<C>,
66
- max_contributors: optional<integer>,
67
- close_by: date,
68
- close_policy: optional<text>,
69
- overfund_policy: optional<text>,
70
- cancel_policy: optional<text>,
71
- fail_policy: optional<text>,
72
- beneficiary_account: optional<account<C>>,
73
- memo: optional<text>,
74
- contribution_instrument: optional<block>,
75
- wording: optional<block>,
76
- funding_join: optional<block>,
77
- ) {
78
- when(wording) {
79
- let(wording_unknown_keys): keys_except(wording, "pool_summary", "pool_description", "create_summary", "commitment_create_summary", "commitment_cancel_summary", "commitment_refund_summary");
80
- when(wording_unknown_keys) {
81
- unsupported {
82
- code: HSX1407;
83
- message: "threshold_pool wording contains an unknown key";
84
- fix: "use only the six declared threshold_pool wording keys";
85
- }
86
- }
87
- }
88
- agent_description: "Reach for threshold pool when many contributors commit toward one target and nothing is collected unless the pool reaches it by the close date. Commitments sit in escrow until the pool activates or fails. Pick weighted distribution to pay a pool out and instant transfer for a single payment.";
89
- when_not(contribution_instrument) {
90
- fields {
91
- [beneficiary]AccountId { type: account<C>; description: concat("The ", words(beneficiary), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
92
- close_by { type: date; description: "Stored close anchor"; }
93
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
94
- maxContributors { type: integer; description: concat("Exactly ", max_contributors, " admitted contributors"); const: max_contributors; }
95
- target { type: money<C>; description: "Funding target in {C} minor units"; }
96
- }
97
- lifecycle {
98
- states open active failed settled;
99
- initial open;
100
- on activate: open -> active;
101
- on fail: open -> failed;
102
- on close: active -> settled;
103
- }
104
- parties { beneficiary: concat(camel(beneficiary), "AccountId"); }
105
- required: [concat(camel(beneficiary), "AccountId"), currency, target, close_by, maxContributors];
106
- title: sentence(instrument);
107
- id_prefix: prefix(instrument);
108
- let(pool_summary): get(wording, "pool_summary");
109
- when_not(pool_summary) { summary: concat("Threshold contribution pool for ", words(beneficiary)); }
110
- when(pool_summary) { summary: pool_summary; }
111
- let(pool_description): get(wording, "pool_description");
112
- when_not(pool_description) { description: "All-or-nothing aggregate contribution threshold"; }
113
- when(pool_description) { description: pool_description; }
114
- aggregate_invariants { childField: amount; childInstrumentId: concat(instrument, "_commitment"); childRefField: concat(camel(instrument), "Id"); childStatuses: [committed]; parentField: target; }
115
- aggregate_invariants { childInstrumentId: concat(instrument, "_commitment"); childRefField: concat(camel(instrument), "Id"); childStatuses: [committed]; count: true; parentField: maxContributors; }
116
- action create {
117
- agent_description: "Open the pool before any commitment exists. The caller supplies the target, the currency, the beneficiary account, the close anchor, and the pinned contributor count. No money moves and the pool opens for commitments.";
118
- let(create_summary): get(wording, "create_summary");
119
- when_not(create_summary) { summary: "Open the contribution pool"; }
120
- when(create_summary) { summary: create_summary; }
121
- moves: [];
122
- steps: [];
123
- }
124
- action activate {
125
- summary: "Activate when commitments meet the target";
126
- due { field: close_by; }
127
- requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [committed]; check: { kind: sum_at_least; amountField: amount; targetField: target; }; }
128
- moves: [];
129
- steps: [];
130
- }
131
- action fail {
132
- summary: "Fail when commitments remain below target";
133
- due { field: close_by; }
134
- requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [committed]; check: { kind: sum_below; amountField: amount; targetField: target; }; }
135
- moves: [];
136
- steps: [];
137
- }
138
- action close {
139
- agent_description: "Settle the pool once every admitted commitment is collected or was cancelled before activation. The pool must be active. No money moves here, collect already paid the beneficiary.";
140
- summary: "Settle after every admitted row is collected or was cancelled before activation";
141
- requires aggregate { instrumentId: concat(instrument, "_commitment"); over: children; refField: concat(camel(instrument), "Id"); statuses: [cancelled, collected]; check: { kind: all_in; }; }
142
- moves: [];
143
- steps: [];
144
- }
145
- instruments {
146
- instrument {
147
- let(parent_name): instrument;
148
- id: concat(parent_name, "_commitment");
149
- generatedPrefix: true;
150
- fields {
151
- amount { type: money<C>; description: "One whole commitment amount"; }
152
- [beneficiary]AccountId { type: account<C>; description: concat("The ", words(beneficiary), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
153
- [parent_name]Id { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
154
- [contributor]AccountId { type: account<C>; description: concat("The ", words(contributor), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
155
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
156
- }
157
- lifecycle {
158
- states committed cancelled collected refunded;
159
- initial committed;
160
- on cancel: committed -> cancelled;
161
- on collect: committed -> collected;
162
- on refund: committed -> refunded;
163
- }
164
- parties { beneficiary: concat(camel(beneficiary), "AccountId"); payer: concat(camel(contributor), "AccountId"); }
165
- required: [concat(camel(beneficiary), "AccountId"), concat(camel(contributor), "AccountId"), amount, currency, concat(camel(parent_name), "Id")];
166
- title: concat(sentence(parent_name), " Commitment");
167
- agent_description: concat("Reach for this child row to record one whole commitment into a ", words(parent_name), ". Each contributor commits once, the money sits in the product escrow, and whether it is collected or refunded depends on the pool reaching its target.");
168
- summary: concat("Whole commitment to ", parent_name);
169
- description: concat("One whole commitment linked to ", parent_name);
170
- action create {
171
- agent_description: "Commit one whole amount into the pool escrow. The pool must still be open, the currency and beneficiary account must match it, and the committed total plus this amount must stay within the target. The money leaves the contributor now.";
172
- let(commitment_create_summary): get(wording, "commitment_create_summary");
173
- when_not(commitment_create_summary) { summary: "Store one whole commitment without exceeding the pool target"; }
174
- when(commitment_create_summary) { summary: commitment_create_summary; }
175
- requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; bind: { [beneficiary]AccountId: concat("fields.", camel(beneficiary), "AccountId"); currency: "fields.currency"; }; }
176
- requires exposure { amountField: amount; anchorField: concat(camel(parent_name), "Id"); capField: target; capOnAnchor: true; childInstrumentId: concat(parent_name, "_commitment"); statuses: [committed]; }
177
- moves: [{ amount: amount; from: payer; to: refs.escrowAccountId; key: commit; }];
178
- steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: "fields.currency"; }; "owner.id": { from: instance; path: productId; }; "owner.type": { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { escrowAccountId: accountId; }; }];
179
- }
180
- action cancel {
181
- agent_description: "Pull one commitment back out of escrow to the contributor. The pool must still be open, so this is unavailable once the pool activates or fails. The money returns and the commitment is closed.";
182
- let(commitment_cancel_summary): get(wording, "commitment_cancel_summary");
183
- when_not(commitment_cancel_summary) { summary: "Cancel one commitment while the pool is open"; }
184
- when(commitment_cancel_summary) { summary: commitment_cancel_summary; }
185
- requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
186
- moves: [{ amount: amount; from: refs.escrowAccountId; to: payer; key: cancel; }];
187
- steps: [];
188
- }
189
- action collect {
190
- agent_description: "Release one commitment out of escrow to the beneficiary. The pool must be active, which happens only once commitments meet the target. The money moves for good.";
191
- summary: "Collect one successful commitment whole";
192
- requires refs { field: concat(camel(parent_name), "Id"); statuses: [active]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
193
- moves: [{ amount: amount; from: refs.escrowAccountId; to: beneficiary; key: collect; }];
194
- steps: [];
195
- }
196
- action refund {
197
- agent_description: "Return one commitment out of escrow to the contributor after the pool failed to reach its target. The pool must be in failed. The money returns and the commitment is closed.";
198
- let(commitment_refund_summary): get(wording, "commitment_refund_summary");
199
- when_not(commitment_refund_summary) { summary: "Refund one failed-pool commitment whole"; }
200
- when(commitment_refund_summary) { summary: commitment_refund_summary; }
201
- requires refs { field: concat(camel(parent_name), "Id"); statuses: [failed]; match: { "fields.[beneficiary]AccountId": concat("fields.", camel(beneficiary), "AccountId"); "fields.currency": "fields.currency"; }; }
202
- moves: [{ amount: amount; from: refs.escrowAccountId; to: payer; key: refund; }];
203
- steps: [];
204
- }
205
- }
206
- }
207
- }
208
-
209
- when(contribution_instrument) {
210
- let(contribution_id): get(contribution_instrument, "instrument");
211
- let(contribution_ref_field): get(contribution_instrument, "ref_field");
212
- let(contribution_amount_field): get(contribution_instrument, "amount_field");
213
- let(committed_status): get(contribution_instrument, "committed_status");
214
- let(collected_status): get(contribution_instrument, "collected_status");
215
- let(instrument_id_prefix): get(contribution_instrument, "id_prefix");
216
- let(active_parked): get(contribution_instrument, "active_parked");
217
- let(custody_ref): get(contribution_instrument, "custody_ref");
218
- let(beneficiary_account_description): get(contribution_instrument, "beneficiary_account_description");
219
- let(target_description): get(contribution_instrument, "target_description");
220
- let(currency_description): get(contribution_instrument, "currency_description");
221
- let(create_examples): get(contribution_instrument, "create_examples");
222
- let(activate_examples): get(contribution_instrument, "activate_examples");
223
- let(fail_examples): get(contribution_instrument, "fail_examples");
224
- let(close_examples): get(contribution_instrument, "close_examples");
225
- let(beneficiary_account_field): camel(beneficiary_account);
226
- let(target_field): camel(target);
227
- let(close_field): camel(close_by);
228
- let(obligation_type): get(funding_join, "obligation");
229
-
230
- fields {
231
- [beneficiary_account_field] { type: account<C>; description: beneficiary_account_description; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
232
- [close_field] { type: date; }
233
- currency { type: text; description: currency_description; maxLength: 3; minLength: 3; pattern: "^[A-Z]{3}$"; }
234
- when(memo) { [memo] { type: text; optional: true; maxLength: 180; minLength: 1; } }
235
- [target_field] { type: money<C>; description: target_description; }
236
- when(funding_join) {
237
- fundingAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
238
- obligationId { type: ref<obligation_type>; optional: true; }
239
- minimumTicket: money<C>;
240
- investorCapBps { type: integer; minimum: 1; maximum: 9999; }
241
- maximumTicket { type: money<C>; optional: true; }
242
- fixedProfit: money<C>;
243
- installmentCount { type: integer; minimum: 1; maximum: 360; }
244
- earningRule { type: text; enum: [per_slice_on_due, on_disbursement]; }
245
- }
246
- }
247
- when(funding_join) { computes derived: { field: maximumTicket; source_field: target_field; rule: { kind: percentage_of; bps: { field: investorCapBps; }; }; rounding: floor; };
248
- }
249
- lifecycle {
250
- states open active failed settled;
251
- initial open;
252
- on activate: open -> active;
253
- on close: active -> settled;
254
- on fail: open -> failed;
255
- }
256
- parties { beneficiary: beneficiary_account_field; }
257
- when_not(funding_join) { required: [beneficiary_account_field, target_field, currency, close_field]; }
258
- when(funding_join) { required: [beneficiary_account_field, target_field, currency, close_field, fundingAccountId, minimumTicket, investorCapBps, fixedProfit, installmentCount, earningRule]; }
259
- aggregate_invariants { childField: contribution_amount_field; childInstrumentId: contribution_id; childRefField: contribution_ref_field; childStatuses: [committed_status, collected_status]; parentField: target_field; }
260
- caller_parked_states { active: active_parked; }
261
- id_prefix: instrument_id_prefix;
262
- action create {
263
- public: none;
264
- agent_description: "Open the pool before any contribution exists. The caller supplies the target, the currency, the beneficiary account, and the close anchor, and the pool provisions its escrow account. No money moves.";
265
- when_not(funding_join) { examples: create_examples; }
266
- moves: [];
267
- steps: [{ operation: "account.escrow.provision"; bind: { currency: { from: instance; path: "fields.currency"; }; "owner.id": { from: instance; path: productId; }; "owner.type": { from: const; value: product; }; productId: { from: instance; path: productId; }; role: { from: const; value: product_escrow; }; }; capture: { [custody_ref]: accountId; }; }];
268
- }
269
- action activate {
270
- public: none;
271
- due { field: close_field; }
272
- examples: activate_examples;
273
- requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [committed_status]; check: { kind: sum_at_least; amountField: contribution_amount_field; targetField: target_field; }; }
274
- moves: [];
275
- steps: [];
276
- }
277
- action fail {
278
- public: none;
279
- due { field: close_field; }
280
- examples: fail_examples;
281
- requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [committed_status]; check: { kind: sum_below; amountField: contribution_amount_field; targetField: target_field; }; }
282
- moves: [];
283
- steps: [];
284
- }
285
- action close {
286
- public: none;
287
- agent_description: "Settle the pool once every contribution has been collected into the beneficiary account. No money moves. Refused while any contribution is still uncollected, so wait for the per-contribution collections to run rather than retrying.";
288
- when_not(funding_join) { examples: close_examples; }
289
- when_not(funding_join) { requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [collected_status]; check: { kind: all_in; }; } }
290
- when(funding_join) {
291
- input: { type: object; additionalProperties: false; properties: { obligationId: { type: string; pattern: get(funding_join, "obligation_pattern"); }; }; required: [obligationId]; };
292
- updates: [obligationId];
293
- requires refs: { field: obligationId; statuses: [created]; match: { "fields.fundingAccountId": fields.fundingAccountId; "fields.currency": fields.currency; }; };
294
- funding: {
295
- obligation_field: obligationId; ticket_instrument_id: contribution_id; ticket_ref_field: contribution_ref_field;
296
- ticket_amount_field: contribution_amount_field; ticket_investor_field: get(funding_join, "investor_field"); ticket_account_field: get(funding_join, "wallet_account_field");
297
- ticket_status: committed_status; collect_action: collect; principal_field: target_field;
298
- source_account_path: concat("refs.", custody_ref); destination_account_field: fundingAccountId;
299
- terms: { [target_field]: principal; fixedProfit: fixedProfit; installmentCount: installmentCount; earningRule: earningRule; [beneficiary_account_field]: borrowerAccountId; currency: currency; };
300
- capture: fundingSnapshot;
301
- };
302
- }
303
- moves: [];
304
- steps: [];
305
- }
306
- }
307
- }
@@ -1,237 +0,0 @@
1
- module std.money_flows.weighted_distribution
2
-
3
- // Frozen largest-remainder distribution splitting one pool across dynamic recipients by recorded weights.
4
- //
5
- // ### Purpose
6
- // `weighted_distribution` distributes dividend pools, creator royalties, liquidation proceeds, and investment returns.
7
- // Child entitlement rows are recorded for each recipient with their respective weight. Once all recipients are recorded,
8
- // an evidence-backed snapshot freezes the entitlement set. Each recipient is then paid their exact largest-remainder
9
- // share such that all payouts sum exactly to `amount` without rounding leaks.
10
- //
11
- // ### Selection guidance
12
- // - vs `pooled_split`: `weighted_distribution` handles dynamic recipient rosters recorded as child rows and frozen
13
- // via snapshot. `pooled_split` hardcodes a fixed set of recipients and static percentages at definition time.
14
- // - vs `settlement_batch`: `weighted_distribution` splits one funding pool out to many recipients.
15
- // `settlement_batch` aggregates many inbound charges and adjustments into a single net payout.
16
- //
17
- // ### Parameters
18
- // - `source`: The funding party providing the distribution pool.
19
- // - `recipient`: The party representing entitled recipients.
20
- // - `amount`: Total distribution pool in minor units of currency `C`.
21
- // - `record_at`: Date when entitlement eligibility is established.
22
- // - `weight`: Binding name for individual recipient weight amounts.
23
- // - `max_recipients`: Exact number of entitlement rows required before snapshotting.
24
- // - `snapshot`: Port freezing the entitlement set against stored decision evidence.
25
- // - `rounding_policy`: Mathematical rounding policy (`largest_remainder`).
26
- // - `withholding_policy`: Tax or withholding policy (`refuse`).
27
- // - `correction_policy`: Error correction policy (`new_distribution`).
28
- // - `flat`: Optional flat distribution configuration block.
29
- // - `id_prefix_override`: Optional custom prefix for generated instrument IDs.
30
- //
31
- // ### Decision ports
32
- // - `snapshot`: Port freezing the entitlement set with an evidence reference, preventing further entries.
33
- //
34
- // ### Example
35
- // ```hsx
36
- // program weighted_distribution_example "Weighted distribution example"
37
- // import { weighted_distribution } from "std/money_flows"
38
- // party distribution_source: business
39
- // party recipient: business
40
- // settlement proceeds = weighted_distribution {
41
- // source: distribution_source
42
- // recipient: recipient
43
- // amount: distributableAmount: money(SAR)
44
- // weight: entitlementWeight: money(SAR)
45
- // max_recipients: 12
46
- // record_at: recordAt
47
- // snapshot: port snapshot_entitlements
48
- // rounding_policy: largest_remainder
49
- // withholding_policy: refuse
50
- // correction_policy: new_distribution
51
- // }
52
- // port snapshot_entitlements {
53
- // allowed: [distribution_source]
54
- // shape: { evidenceReference: text }
55
- // }
56
- // ```
57
- // ### Receipt-backed distributions
58
- // receipts selects repeated distributions from immutable allocation receipts.
59
- // It names round, receipt, path, mode (cash or loss), fee_bps and vat_bps.
60
- // The funding snapshot owns ticket weights and original wallet destinations.
61
- // Cash floors each ratio after fee and fee VAT; a named account receives residual.
62
- // Loss assigns principal by largest remainder without cash, fee or VAT.
63
- // Each receipt is consumed once; a new receipt may fund a new distribution record.
64
- // This mode does not use caller-entered weights or a once-only settled principal pool.
65
- //
66
- export instrument weighted_distribution<C>(
67
- source: optional<party>,
68
- recipient: optional<party>,
69
- amount: optional<money<C>>,
70
- record_at: optional<date>,
71
- weight: optional<money<C>>,
72
- max_recipients: optional<integer>,
73
- snapshot: optional<condition>,
74
- rounding_policy: optional<text>,
75
- withholding_policy: optional<text>,
76
- correction_policy: optional<text>,
77
- flat: optional<block>,
78
- receipts: optional<block>,
79
- id_prefix_override: optional<text>,
80
- ) {
81
- when_not(receipts) {
82
- when_not(flat) {
83
- fields {
84
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
85
- amount { type: money<C>; description: "Distribution pool in {C} minor units"; }
86
- [source]AccountId { type: account<C>; description: concat("The ", words(source), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
87
- maxRecipients { type: integer; description: concat("Exactly ", max_recipients, " frozen entitlement rows"); const: max_recipients; }
88
- record_at { type: date; description: "Stored record date"; }
89
- }
90
- lifecycle { states open snapshotted; initial open; on [snapshot]: open -> snapshotted; }
91
- parties { payer: concat(camel(source), "AccountId"); }
92
- required: [concat(camel(source), "AccountId"), currency, amount, record_at, maxRecipients];
93
- title: sentence(instrument);
94
- id_prefix: prefix(instrument);
95
- agent_description: "Reach for weighted distribution when one pool splits across many recipients by declared weights. Record one entitlement per recipient, freeze the set with the snapshot port, then each entitlement pays its own largest-remainder share, so the shares add back to the pool exactly. Settlement batch goes the other way, many charges into one payout.";
96
- summary: "Frozen largest-remainder distribution";
97
- description: "Evidence-frozen weighted distribution";
98
- aggregate_invariants { childInstrumentId: concat(instrument, "_entitlement"); childRefField: concat(camel(instrument), "Id"); childStatuses: [recorded, paid]; count: true; parentField: maxRecipients; }
99
- action create { agent_description: "Open a distribution and start recording entitlements. No money moves. The pool amount, the record date, and the exact recipient count are fixed here."; summary: "Open entitlement recording"; moves: []; steps: []; }
100
- action [snapshot] {
101
- agent_description: "Freeze the entitlement set against a stated evidence reference. The distribution must still be open and its recorded entitlements must match the declared recipient count. Nothing can be recorded afterwards and there is no unfreeze. No money moves.";
102
- summary: "Freeze the entitlement set from stored evidence";
103
- port { allowedParties: [payer]; }
104
- input { type: object; properties: { evidenceReference: { type: string; description: "Required decision reference retained in the operation receipt"; minLength: 1; maxLength: 180; }; }; required: [evidenceReference]; additionalProperties: false; }
105
- capture_input { snapshotEvidenceReference: evidenceReference; }
106
- moves: [];
107
- steps: [];
108
- }
109
- instruments {
110
- instrument {
111
- let(parent_name): instrument;
112
- id: concat(parent_name, "_entitlement");
113
- generatedPrefix: true;
114
- fields {
115
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
116
- [source]AccountId { type: account<C>; description: concat("The ", words(source), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
117
- [weight] { type: money<C>; description: "Stored non-negative entitlement weight"; }
118
- [parent_name]Id { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
119
- [recipient]AccountId { type: account<C>; description: concat("The ", words(recipient), " account"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
120
- }
121
- lifecycle { states recorded paid; initial recorded; on payout: recorded -> paid; }
122
- parties { beneficiary: concat(camel(recipient), "AccountId"); payer: concat(camel(source), "AccountId"); }
123
- required: [concat(camel(recipient), "AccountId"), concat(camel(source), "AccountId"), currency, concat(camel(parent_name), "Id"), weight];
124
- title: concat(sentence(parent_name), " Entitlement");
125
- agent_description: concat("One recipient weighted claim on ", parent_name, ". Record one per recipient before the snapshot. Its share is derived from its weight against the frozen set, never supplied by the caller.");
126
- summary: concat("Frozen entitlement in ", parent_name);
127
- description: concat("One frozen weighted entitlement in ", parent_name);
128
- action create {
129
- agent_description: "Record one recipient weight against an open distribution. The parent distribution must still be open, and the currency and source account are copied from it. No money moves. Do not supply a share. It is derived later from the frozen set.";
130
- summary: "Record one entitlement before snapshot";
131
- requires refs { field: concat(camel(parent_name), "Id"); statuses: [open]; bind: { currency: "fields.currency"; [source]AccountId: concat("fields.", camel(source), "AccountId"); }; }
132
- moves: [];
133
- steps: [];
134
- }
135
- action payout {
136
- agent_description: "Pay this recipient their largest-remainder share of the pool. The parent distribution must already be snapshotted. The amount is computed from the recorded weights, not supplied by the caller, and a zero share moves nothing. This moves money and does not reverse.";
137
- summary: "Pay the deterministic largest-remainder share once";
138
- requires refs { field: concat(camel(parent_name), "Id"); statuses: [snapshotted]; match: { "fields.currency": "fields.currency"; "fields.[source]AccountId": concat("fields.", camel(source), "AccountId"); }; }
139
- computes distribute { amountRef: payoutShare; onZero: skip_steps; pool: { from: parent; path: concat("fields.", amount); }; refField: concat(camel(parent_name), "Id"); statuses: [recorded, paid]; weightField: weight; }
140
- moves: [{ amount: refs.payoutShare; from: payer; to: beneficiary; key: payout; }];
141
- steps: [];
142
- }
143
- }
144
- }
145
- }
146
- when(flat) {
147
- let(pool): get(flat, "pool");
148
- let(pool_ref): get(pool, "field");
149
- let(pool_ref_field): camel(pool_ref);
150
- let(pool_instrument): get(pool, "instrument");
151
- let(pool_create_statuses): get(pool, "create_statuses");
152
- let(pool_payout_statuses): get(pool, "payout_statuses");
153
- let(pool_bind): get(pool, "bind");
154
- let(pool_match): get(pool, "match");
155
- let(pool_amount_path): get(pool, "amount_path");
156
- let(weight_source): get(flat, "weight_source");
157
- let(weight_ref): get(weight_source, "field");
158
- let(weight_ref_field): camel(weight_ref);
159
- let(weight_statuses): get(weight_source, "statuses");
160
- let(weight_bind): get(weight_source, "bind");
161
- let(weight_match): get(weight_source, "match");
162
- let(unique_weight_source): get(weight_source, "unique");
163
- let(group): get(flat, "group");
164
- let(group_ref): get(group, "field");
165
- let(source_account_field): camel(concat(source, "_account_id"));
166
- let(recipient_account_field): camel(concat(recipient, "_account_id"));
167
- fields {
168
- [amount] { type: money<C>; description: get(flat, "amount_description"); }
169
- [record_at] { type: date; }
170
- [pool_ref] { type: pool_ref; description: get(pool, "description"); pattern: get(pool, "pattern"); }
171
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
172
- [weight_ref] { type: weight_ref; description: get(weight_source, "description"); pattern: get(weight_source, "pattern"); }
173
- [group_ref] { type: group_ref; description: get(group, "description"); pattern: get(group, "pattern"); }
174
- [recipient]AccountId { type: account<C>; description: get(flat, "recipient_account_description"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
175
- [source]AccountId { type: account<C>; description: get(flat, "source_account_description"); "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
176
- }
177
- lifecycle { states claimed paid; initial claimed; on payout: claimed -> paid; }
178
- parties { beneficiary: recipient_account_field; }
179
- required: get(flat, "required");
180
- title: sentence(instrument);
181
- id_prefix: if_eq(id_prefix_override, false, prefix(instrument), id_prefix_override);
182
- agent_description: concat("One weighted claim on a distribution pool that another instrument owns. Record one claim per recipient before the pool settles. Its share is derived from its weight against the settled pool, never supplied by the caller, and the shares add back to the pool exactly.");
183
- summary: "Flat largest-remainder distribution claim";
184
- description: "One weighted claim on a settled distribution pool";
185
- action create {
186
- public: none;
187
- agent_description: concat("Record one recipient weight against a pool that is still open. Both the pool and the weight source must be in an accepting state, and the same weight source cannot be claimed twice. No money moves. Do not supply a share. It is derived at payout from the settled pool.");
188
- summary: "Open one weighted distribution claim";
189
- requires_refs: [
190
- { field: pool_ref_field; statuses: pool_create_statuses; bind: pool_bind; match: pool_match; },
191
- { field: weight_ref_field; statuses: weight_statuses; bind: weight_bind; match: weight_match; unique: unique_weight_source; }
192
- ];
193
- moves: [];
194
- steps: [];
195
- }
196
- action payout {
197
- public: none;
198
- summary: "Pay one largest-remainder share from the settled pool";
199
- due { field: record_at; whenParentStatus: { instrumentId: pool_instrument; refField: pool_ref_field; statuses: pool_payout_statuses; }; }
200
- requires refs { field: pool_ref_field; statuses: pool_payout_statuses; }
201
- computes distribute { amountRef: payoutShare; onZero: skip_steps; pool: { from: parent; path: pool_amount_path; }; refField: pool_ref_field; statuses: [claimed, paid]; weightField: amount; }
202
- moves: [{ key: transfer; operation: internal_transfer.create; bind: { amount: { from: instance; path: refs.payoutShare; }; currency: { from: instance; path: fields.currency; }; destinationAccountId: { from: instance; path: concat("fields.", recipient_account_field); }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", source_account_field); }; }; capture: { payoutTransferId: transferId; }; }];
203
- steps: [];
204
- }
205
- }
206
- }
207
- when(receipts) {
208
- let(round_type): get(receipts, "round");
209
- let(receipt_type): get(receipts, "receipt");
210
- fields {
211
- roundId: ref<round_type>;
212
- receiptId: ref<receipt_type>;
213
- currency { type: text; pattern: "^[A-Z]{3}$"; minLength: 3; maxLength: 3; }
214
- feeAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
215
- taxAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
216
- residualAccountId { type: account<C>; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
217
- }
218
- parties { fee: feeAccountId; tax: taxAccountId; beneficiary: residualAccountId; }
219
- lifecycle { states distributed; initial distributed; }
220
- title: sentence(instrument);
221
- summary: "Receipt-backed frozen-weight distribution";
222
- agent_description: "Consume one allocation receipt against the round's frozen tickets. Cash distributions carve fee and VAT, floor each ticket share and pay the residual to its named owner. Loss mode records principal loss without cash.";
223
- action create {
224
- public: none;
225
- agent_description: "Distribute one receipt once. All amounts and sources come from the consumed receipt and frozen funding snapshot.";
226
- requires refs: { field: roundId; statuses: [settled]; match: { "fields.currency": fields.currency; }; };
227
- receipt distribution: {
228
- round_field: roundId; snapshot_ref: fundingSnapshot;
229
- receipt_field: receiptId; receipt_path: get(receipts, "path"); mode: get(receipts, "mode");
230
- fee_bps: get(receipts, "fee_bps"); vat_bps: get(receipts, "vat_bps");
231
- fee_account_field: feeAccountId; tax_account_field: taxAccountId; residual_account_field: residualAccountId;
232
- capture: distributionReceipt;
233
- };
234
- steps: [];
235
- }
236
- }
237
- }