@hyperscale0/hsx 2.4.1 → 3.0.1

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 +4 -406
  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 +77 -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 +78 -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 -4160
  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 -5515
  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,305 +0,0 @@
1
- module std.money_flows.rotating_pool
2
-
3
- // Rotating savings and credit association (ROSCA) pool where members contribute fixed amounts and take turns receiving the pot.
4
- //
5
- // ### Purpose
6
- // `rotating_pool` coordinates peer savings circles, chit funds, tandas, and committee savings groups.
7
- // A fixed group of members contributes an identical contribution amount each cycle. In each cycle, one designated member
8
- // receives the entire pooled pot according to a predefined `payout_order` until all members have taken their turn.
9
- //
10
- // ### Selection guidance
11
- // - vs `threshold_pool`: `rotating_pool` coordinates recurring multi-party peer savings with rotating payouts.
12
- // `threshold_pool` is all-or-nothing capital accumulation toward a single threshold for one beneficiary.
13
- // - vs `scheduled`: `scheduled` coordinates a single payer to a single payee. `rotating_pool` orchestrates
14
- // a closed circular group of members taking sequential turns.
15
- //
16
- // ### Parameters
17
- // - `members`: List of parties belonging to the rotating group.
18
- // - `contribution`: Fixed contribution amount required from each member per cycle in minor units of currency `C`.
19
- // - `count`: Total number of cycles in the rotation (matching the member count).
20
- // - `every`: Recurrence interval between contribution cycles (e.g. `"P30D"`).
21
- // - `first_due`: Due date for the first cycle's contribution.
22
- // - `payout_order`: Ordered list of member parties defining the cycle payout sequence.
23
- // - `default_policy`: Policy for handling missed contributions (`due_condition`).
24
- // - `guarantee_policy`: Policy for backing defaulted contributions (`funded_only`).
25
- // - `guarantor`: Optional guarantor party covering member defaults.
26
- // - `exit_policy`: Policy governing member departures (`before_activation_only`).
27
- // - `memo`: Optional memo text stored on the pool.
28
- // - `membership`: Optional custom membership configuration block.
29
- //
30
- // ### Decision ports
31
- // None. Cycle advancement and pot payouts follow the declared schedule and membership actions.
32
- //
33
- // ### Example
34
- // ```hsx
35
- // program rotating_pool_example "Rotating pool example"
36
- // import { rotating_pool } from "std/money_flows"
37
- // party member_a: person
38
- // party member_b: person
39
- // party member_c: person
40
- // party guarantor: business
41
- // settlement pool = rotating_pool {
42
- // members: [member_a, member_b, member_c]
43
- // contribution: contributionAmount: money(SAR)
44
- // count: 3
45
- // every: P30D
46
- // first_due: firstContributionAt
47
- // payout_order: [member_b, member_c, member_a]
48
- // default_policy: due_condition
49
- // guarantee_policy: funded_only
50
- // guarantor: guarantor
51
- // exit_policy: before_activation_only
52
- // }
53
- // ```
54
- export instrument rotating_pool<C>(members: optional<list<party>>, contribution: money<C>, count: integer, every: optional<text>, first_due: date, payout_order: optional<list<party>>, default_policy: optional<text>, guarantee_policy: optional<text>, guarantor: optional<party>, exit_policy: optional<text>, memo: optional<text>, membership: optional<block>) {
55
- when_not(membership) {
56
- let(member_fields): suffix_each(members, "AccountId");
57
- let(guarantor_field): concat(camel(guarantor), "AccountId");
58
- let(contribution_field): camel(contribution);
59
- let(first_due_field): camel(first_due);
60
- let(parent_ref_field): concat(camel(instrument), "Id");
61
- let(reference_filter): "x-hyperscale-reference-filter";
62
- fields {
63
- for member in members { [member]AccountId { type: account<C>; description: concat("The ", words(member), " account"); [reference_filter]: { column: role; values: [customer_balance]; }; } }
64
- [guarantor_field] { type: account<C>; description: concat("The ", words(guarantor), " account"); [reference_filter]: { column: role; values: [customer_balance]; }; }
65
- [contribution_field] { type: money<C>; description: "Exact contribution in {C} minor units"; }
66
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
67
- [first_due_field] { type: date; description: "Stored first contribution due date"; }
68
- one { type: integer; description: "Exact fixed member-row count"; const: 1; }
69
- }
70
- lifecycle {
71
- states forming cancelled;
72
- for cycle in count {
73
- let(active_state): concat("active_cycle_", cycle);
74
- let(ready_state): concat("cycle_", cycle, "_ready");
75
- let(states_key): "states";
76
- [states_key]: [active_state, ready_state];
77
- }
78
- states completed;
79
- initial forming;
80
- on cancel: forming -> cancelled;
81
- on activate: forming -> active_cycle_1;
82
- for cycle in count {
83
- let(active_state): concat("active_cycle_", cycle);
84
- let(ready_state): concat("cycle_", cycle, "_ready");
85
- on ready_cycle_[cycle]: active_state -> ready_state;
86
- let(next_state): if_eq(cycle, count, "completed", concat("active_cycle_", add(cycle, 1)));
87
- on advance_cycle_[cycle]: ready_state -> next_state;
88
- }
89
- }
90
- parties { payer: guarantor_field; }
91
- required: concat_lists(member_fields, [guarantor_field, "currency", contribution_field, first_due_field, "one"]);
92
- title: sentence(instrument);
93
- id_prefix: prefix(instrument);
94
- agent_description: "Reach for rotating pool when a fixed group pays the same contribution every cycle and one member takes the whole pot each cycle, in a declared order. The roster freezes at creation, one contribution row per member, and the pot rotates until every member has been paid. Scheduled is the one payer, one payee shape on a calendar.";
95
- summary: concat(count, "-member rotating pool");
96
- description: "Fixed rotating contribution and payout order";
97
- action create { agent_description: "Create the pool with its frozen roster, cycle count, contribution amount, and payout order. No money moves. Each member's contribution row is created separately, and the pool cannot activate until every row exists."; summary: "Create the fixed roster before activation"; moves: []; steps: []; }
98
- action cancel { agent_description: "Cancel the pool before it activates. Only a forming pool cancels, no money has moved yet, and there is nothing to refund. Once the pool activates this exit is gone."; summary: "Cancel before activation without moving money"; moves: []; steps: []; }
99
- action activate {
100
- agent_description: "Start cycle one. Refused unless every member on the roster has exactly one contribution row waiting at the first cycle, so a pool can never run a member short. No money moves.";
101
- summary: "Activate only after every fixed member row exists once";
102
- for member in members { requires aggregate { instrumentId: concat(instrument, "_", member, "_contribution"); over: children; refField: parent_ref_field; statuses: [cycle_1_due]; check: { kind: count_equals_field; field: one; }; } }
103
- moves: [];
104
- steps: [];
105
- }
106
- for cycle in count {
107
- action ready_cycle_[cycle] {
108
- agent_description: concat("Lock cycle ", cycle, " once every member row is funded or covered by the guarantor. Refused while any row is still due or defaulted. No money moves here. Locking the cycle is what lets its payouts run.");
109
- summary: concat("Lock cycle ", cycle, " only after every member row is funded or guaranteed");
110
- let(funded_state): concat("cycle_", cycle, "_funded");
111
- let(guaranteed_state): concat("cycle_", cycle, "_guaranteed");
112
- for member in members { requires aggregate { instrumentId: concat(instrument, "_", member, "_contribution"); over: children; refField: parent_ref_field; statuses: [funded_state, guaranteed_state]; check: { kind: all_in; }; } }
113
- moves: [];
114
- steps: [];
115
- }
116
- action advance_cycle_[cycle] {
117
- let(target_status): if_eq(cycle, count, "completed", concat("cycle_", add(cycle, 1), "_due"));
118
- agent_description: concat("Close cycle ", cycle, " once every member row has paid into that cycle pot, moving the pool to the next cycle or to completed on the last one. Refused while any row is unpaid. No money moves here. The money moved on the member rows.");
119
- summary: if_eq(cycle, count, "Complete after the final shared pot pays", concat("Advance after every cycle ", cycle, " contribution pays"));
120
- for member in members { requires aggregate { instrumentId: concat(instrument, "_", member, "_contribution"); over: children; refField: parent_ref_field; statuses: [target_status]; check: { kind: all_in; }; } }
121
- moves: [];
122
- steps: [];
123
- }
124
- }
125
- instruments {
126
- for member in members {
127
- instrument {
128
- let(parent_name): instrument;
129
- let(child_id): concat(parent_name, "_", member, "_contribution");
130
- id: child_id;
131
- generatedPrefix: true;
132
- title: concat(sentence(parent_name), " ", sentence(member), " Contribution");
133
- agent_description: concat("One seat in the pool. The ", words(member), " pays the same contribution every cycle and takes the whole pot on their turn. Every contribution, default, guarantee, and payout for that member happens on this row, never on the pool itself.");
134
- summary: concat(words(member), " contribution row");
135
- description: concat("Fixed contribution row for ", words(member));
136
- fields {
137
- for roster_member in payout_order { [roster_member]AccountId { type: account<C>; description: concat("The ", words(roster_member), " account"); [reference_filter]: { column: role; values: [customer_balance]; }; } }
138
- [guarantor_field] { type: account<C>; description: concat("The ", words(guarantor), " account"); [reference_filter]: { column: role; values: [customer_balance]; }; }
139
- [contribution_field] { type: money<C>; description: "Exact contribution amount shared by every cycle"; }
140
- currency { type: text; description: "ISO 4217 currency code"; minLength: 3; maxLength: 3; pattern: "^[A-Z]{3}$"; }
141
- [first_due_field] { type: date; description: "First due anchor derived from the pool"; }
142
- [parent_ref_field] { type: ref<parent_name>; description: concat("The exact ", parent_name); pattern: concat("^", prefix(parent_name), "_(sandbox|live)_[a-z0-9]{8,64}$"); }
143
- }
144
- lifecycle {
145
- for cycle in count {
146
- let(states_key): "states";
147
- let(due_state): concat("cycle_", cycle, "_due");
148
- let(funded_state): concat("cycle_", cycle, "_funded");
149
- let(defaulted_state): concat("cycle_", cycle, "_defaulted");
150
- let(guaranteed_state): concat("cycle_", cycle, "_guaranteed");
151
- [states_key]: [due_state, funded_state, defaulted_state, guaranteed_state];
152
- }
153
- states final_paid completed;
154
- initial cycle_1_due;
155
- for cycle in count {
156
- let(due_state): concat("cycle_", cycle, "_due");
157
- let(funded_state): concat("cycle_", cycle, "_funded");
158
- let(defaulted_state): concat("cycle_", cycle, "_defaulted");
159
- let(guaranteed_state): concat("cycle_", cycle, "_guaranteed");
160
- on contribute_cycle_[cycle]: due_state -> funded_state;
161
- on mark_default_cycle_[cycle]: due_state -> defaulted_state;
162
- on guarantee_cycle_[cycle]: defaulted_state -> guaranteed_state;
163
- let(next_state): if_eq(cycle, count, "final_paid", concat("cycle_", add(cycle, 1), "_due"));
164
- on pay_cycle_[cycle]: funded_state -> next_state;
165
- on pay_guaranteed_cycle_[cycle]: guaranteed_state -> next_state;
166
- }
167
- on close: final_paid -> completed;
168
- }
169
- parties { beneficiary: concat(camel(member), "AccountId"); payer: guarantor_field; }
170
- action create {
171
- agent_description: concat("Create the contribution row for the ", words(member), " and provision its escrow account. The pool must still be forming, and every member needs exactly one row before the pool can activate. No money moves.");
172
- summary: concat("Create the fixed contribution row for ", words(member));
173
- requires refs { field: parent_ref_field; statuses: [forming]; unique: true; bind: { [contribution_field]: concat("fields.", contribution_field); currency: "fields.currency"; [first_due_field]: concat("fields.", first_due_field); [guarantor_field]: concat("fields.", guarantor_field); for roster_member in members { let(roster_field): concat(camel(roster_member), "AccountId"); [roster_field]: concat("fields.", roster_field); } }; }
174
- moves: [];
175
- 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; }; }];
176
- }
177
- for cycle in count {
178
- let(active_state): concat("active_cycle_", cycle);
179
- let(ready_state): concat("cycle_", cycle, "_ready");
180
- let(due_offset): scale_duration(every, sub(cycle, 1));
181
- let(contribute_name): concat("contribute_cycle_", cycle);
182
- let(guarantee_name): concat("guarantee_cycle_", cycle);
183
- let(pay_name): concat("pay_cycle_", cycle);
184
- let(pay_guaranteed_name): concat("pay_guaranteed_cycle_", cycle);
185
- let(contribute_capture): concat("contributeCycle", cycle, "ContributionTransferId");
186
- let(guarantee_capture): concat("guaranteeCycle", cycle, "GuaranteeTransferId");
187
- let(pay_capture): concat("payCycle", cycle, "PayoutTransferId");
188
- let(pay_guaranteed_capture): concat("payGuaranteedCycle", cycle, "PayoutTransferId");
189
- let(payout_member): at(payout_order, cycle);
190
- let(payout_field): concat(camel(payout_member), "AccountId");
191
- action contribute_cycle_[cycle] {
192
- summary: concat("Fund ", words(member), "'s cycle ", cycle, " contribution");
193
- let(first): if_eq(cycle, 1, true, false);
194
- when(first) { due: { field: first_due_field; }; sandbox_failure_point: funding; }
195
- when_not(first) { due: { field: first_due_field; offset: due_offset; }; }
196
- requires refs { field: parent_ref_field; statuses: [active_state]; }
197
- moves: [{ key: "contribution"; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", contribution_field); }; currency: { from: instance; path: "fields.currency"; }; destinationAccountId: { from: instance; path: "refs.escrowAccountId"; }; "metadata.instrumentId": { from: const; value: child_id; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: contribute_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", camel(member), "AccountId"); }; }; capture: { [contribute_capture]: transferId; }; }];
198
- steps: [];
199
- }
200
- action mark_default_cycle_[cycle] {
201
- summary: concat("Mark the stored cycle ", cycle, " due condition");
202
- let(first): if_eq(cycle, 1, true, false);
203
- when(first) { due: { field: first_due_field; }; }
204
- when_not(first) { due: { field: first_due_field; offset: due_offset; }; }
205
- requires refs { field: parent_ref_field; statuses: [active_state]; }
206
- moves: [];
207
- steps: [];
208
- }
209
- action guarantee_cycle_[cycle] {
210
- agent_description: concat("Have the guarantor fund cycle ", cycle, " for this member after the row defaulted. The row must be marked defaulted for that cycle. This moves the guarantor's money into the row escrow so the pot still pays in full.");
211
- summary: concat("Fund the defaulted cycle ", cycle, " amount before payout");
212
- requires refs { field: parent_ref_field; statuses: [active_state]; }
213
- moves: [{ key: guarantee; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", contribution_field); }; currency: { from: instance; path: "fields.currency"; }; destinationAccountId: { from: instance; path: "refs.escrowAccountId"; }; "metadata.instrumentId": { from: const; value: child_id; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: guarantee_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: concat("fields.", guarantor_field); }; }; capture: { [guarantee_capture]: transferId; }; }];
214
- steps: [];
215
- }
216
- action pay_cycle_[cycle] {
217
- agent_description: concat("Pay this row's funded cycle ", cycle, " contribution out of escrow to whoever holds the pot that cycle. The cycle must be locked first and the row must be funded rather than guaranteed. This moves money and does not reverse.");
218
- summary: concat("Pay this member's stored contribution into cycle ", cycle, "'s shared pot recipient");
219
- requires refs { field: parent_ref_field; statuses: [ready_state]; }
220
- moves: [{ key: payout; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", contribution_field); }; currency: { from: instance; path: "fields.currency"; }; destinationAccountId: { from: instance; path: concat("fields.", payout_field); }; "metadata.instrumentId": { from: const; value: child_id; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: pay_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: "refs.escrowAccountId"; }; }; capture: { [pay_capture]: transferId; }; }];
221
- let(first): if_eq(cycle, 1, true, false);
222
- when(first) { sandbox_failure_point: "release"; }
223
- steps: [];
224
- }
225
- action pay_guaranteed_cycle_[cycle] {
226
- agent_description: concat("Pay this row's guarantor-funded cycle ", cycle, " contribution out of escrow to whoever holds the pot that cycle. The cycle must be locked first and the row must be guaranteed rather than funded. This moves money and does not reverse.");
227
- summary: concat("Pay the funded default into cycle ", cycle, "'s stored recipient");
228
- requires refs { field: parent_ref_field; statuses: [ready_state]; }
229
- moves: [{ key: payout; operation: internal_transfer.create; bind: { amount: { from: instance; path: concat("fields.", contribution_field); }; currency: { from: instance; path: "fields.currency"; }; destinationAccountId: { from: instance; path: concat("fields.", payout_field); }; "metadata.instrumentId": { from: const; value: child_id; }; "metadata.instrumentInstanceId": { from: instance; path: instrumentInstanceId; }; "metadata.phase": { from: const; value: pay_guaranteed_name; }; productId: { from: instance; path: productId; }; sourceAccountId: { from: instance; path: "refs.escrowAccountId"; }; }; capture: { [pay_guaranteed_capture]: transferId; }; }];
230
- steps: [];
231
- }
232
- }
233
- action close { agent_description: "Retire this member row after its final payout. Refused while the row escrow account still holds anything, so a row can never close over stranded money. No money moves."; summary: "Complete after the final payout drains this member custody"; requires drained: { path: "refs.escrowAccountId"; }; moves: []; steps: []; }
234
- }
235
- }
236
- }
237
- }
238
-
239
- when(membership) {
240
- let(membership_instrument): get(membership, "instrument");
241
- let(membership_ref_field): get(membership, "ref_field");
242
- let(joined_status): get(membership, "joined_status");
243
- let(active_status): get(membership, "active_status");
244
- let(paid_status): get(membership, "paid_status");
245
- let(custody_ref): get(membership, "custody_ref");
246
- let(instrument_id_prefix): get(membership, "id_prefix");
247
- let(active_parked): get(membership, "active_parked");
248
- let(min_members): get(membership, "min_members");
249
- let(max_members): get(membership, "max_members");
250
- let(contribution_description): get(membership, "contribution_description");
251
- let(currency_description): get(membership, "currency_description");
252
- let(create_examples): get(membership, "create_examples");
253
- let(activate_examples): get(membership, "activate_examples");
254
- let(disband_examples): get(membership, "disband_examples");
255
- let(close_examples): get(membership, "close_examples");
256
- let(contribution_field): camel(contribution);
257
- let(count_field): camel(count);
258
- let(first_due_field): camel(first_due);
259
-
260
- fields {
261
- [contribution_field] { type: money<C>; description: contribution_description; }
262
- currency { type: text; description: currency_description; maxLength: 3; minLength: 3; pattern: "^[A-Z]{3}$"; }
263
- [count_field] { type: integer; maximum: max_members; minimum: min_members; }
264
- when(memo) { [memo] { type: text; optional: true; maxLength: 180; minLength: 1; } }
265
- [first_due_field] { type: date; }
266
- }
267
- lifecycle {
268
- states forming active completed disbanded;
269
- initial forming;
270
- on activate: forming -> active;
271
- on close: active -> completed;
272
- on disband: forming -> disbanded;
273
- }
274
- required: [count_field, contribution_field, currency, first_due_field];
275
- aggregate_invariants { childInstrumentId: membership_instrument; childRefField: membership_ref_field; childStatuses: [joined_status, active_status, paid_status]; count: true; parentField: count_field; }
276
- caller_parked_states { active: active_parked; }
277
- agent_description: concat("Reach for ", words(instrument), " when a fixed group pays the same contribution every cycle and one member takes the whole pot each cycle, and the product publishes its own membership rows. This shape owns the pot account and the seat count. Every contribution and payout happens on the membership row that references it, never here.");
278
- id_prefix: instrument_id_prefix;
279
- action create {
280
- public: none;
281
- agent_description: concat("Open the ", words(instrument), " and provision the pot account every contribution lands in. No money moves. The roster is still empty, so the pool stays forming until one membership row exists per seat.");
282
- examples: create_examples;
283
- moves: [];
284
- 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; }; }];
285
- }
286
- action activate {
287
- public: none;
288
- due { field: first_due_field; }
289
- examples: activate_examples;
290
- requires aggregate { instrumentId: membership_instrument; over: children; refField: membership_ref_field; statuses: [joined_status]; check: { kind: count_equals_field; field: count_field; }; }
291
- moves: [];
292
- steps: [];
293
- }
294
- action disband { public: none; agent_description: concat("Close a ", words(instrument), " that never filled its roster. Only a forming pool disbands, no money has moved, and there is nothing to refund. Once the pool activates this exit is gone."); examples: disband_examples; moves: []; steps: []; }
295
- action close {
296
- public: none;
297
- agent_description: concat("Retire the ", words(instrument), " after every membership row has been paid. Refused while any row is unpaid or while the pot account still holds anything, so a pool can never close over stranded money. No money moves.");
298
- examples: close_examples;
299
- requires aggregate { instrumentId: membership_instrument; over: children; refField: membership_ref_field; statuses: [paid_status]; check: { kind: all_in; }; }
300
- requires drained { path: concat("refs.", custody_ref); }
301
- moves: [];
302
- steps: [];
303
- }
304
- }
305
- }