@hyperscale0/hsx 2.1.1 → 2.2.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 (110) hide show
  1. package/CHANGELOG.md +38 -7
  2. package/dist/src/cli.d.ts +1 -1
  3. package/dist/src/cli.d.ts.map +1 -1
  4. package/dist/src/cli.js +39 -5
  5. package/dist/src/cli.js.map +1 -1
  6. package/dist/src/compile.d.ts +0 -1
  7. package/dist/src/compile.d.ts.map +1 -1
  8. package/dist/src/compile.js +15 -1
  9. package/dist/src/compile.js.map +1 -1
  10. package/dist/src/cost.d.ts +3 -3
  11. package/dist/src/cost.d.ts.map +1 -1
  12. package/dist/src/cost.js +22 -12
  13. package/dist/src/cost.js.map +1 -1
  14. package/dist/src/parse.d.ts.map +1 -1
  15. package/dist/src/parse.js +10 -14
  16. package/dist/src/parse.js.map +1 -1
  17. package/dist/src/std-bundle.d.ts.map +1 -1
  18. package/dist/src/std-bundle.js +20 -84
  19. package/dist/src/std-bundle.js.map +1 -1
  20. package/dist/src/typecheck.d.ts.map +1 -1
  21. package/dist/src/typecheck.js +171 -39
  22. package/dist/src/typecheck.js.map +1 -1
  23. package/dist/src/version.d.ts +1 -1
  24. package/dist/src/version.js +1 -1
  25. package/docs/README.md +4 -2
  26. package/docs/guide/01-first-program.md +1 -1
  27. package/docs/guide/03-instruments.md +14 -1
  28. package/docs/guide/06-schedules.md +5 -1
  29. package/docs/guide/08-writing-a-module.md +48 -0
  30. package/docs/llms-full.txt +1966 -422
  31. package/docs/llms.txt +3 -4
  32. package/docs/reference/cli.md +6 -5
  33. package/docs/reference/diagnostics.md +9 -9
  34. package/docs/reference/grammar.md +2 -3
  35. package/docs/reference/std/advance.md +94 -17
  36. package/docs/reference/std/cancellable_booking.md +138 -17
  37. package/docs/reference/std/captured_payment.md +96 -24
  38. package/docs/reference/std/conditional_disbursement.md +82 -14
  39. package/docs/reference/std/credit_facility.md +89 -16
  40. package/docs/reference/std/held_payment.md +155 -55
  41. package/docs/reference/std/instant_transfer.md +79 -12
  42. package/docs/reference/std/metered.md +71 -9
  43. package/docs/reference/std/pooled_split.md +76 -7
  44. package/docs/reference/std/premium_forward.md +100 -20
  45. package/docs/reference/std/reconciled_payout.md +84 -14
  46. package/docs/reference/std/rotating_pool.md +112 -24
  47. package/docs/reference/std/scheduled.md +117 -32
  48. package/docs/reference/std/security_deposit.md +119 -27
  49. package/docs/reference/std/settlement_batch.md +105 -24
  50. package/docs/reference/std/swap.md +113 -26
  51. package/docs/reference/std/threshold_pool.md +120 -29
  52. package/docs/reference/std/weighted_distribution.md +117 -21
  53. package/docs/reference/types.md +39 -15
  54. package/docs/reference/udl-output.md +6 -6
  55. package/examples/01-first-program/README.md +1 -1
  56. package/examples/{02-imports-and-archetypes → 02-imports-and-modules}/README.md +1 -1
  57. package/examples/{02-imports-and-archetypes → 02-imports-and-modules}/photo-booth.hsx +1 -1
  58. package/examples/04-complete-product/README.md +1 -1
  59. package/examples/05-authored-instrument/README.md +5 -0
  60. package/examples/05-authored-instrument/payment.hsx +37 -0
  61. package/examples/05-watch-club/watch-club.hsx +0 -1
  62. package/examples/README.md +1 -2
  63. package/examples/advance/advance.udl +29 -6
  64. package/examples/cancellable_booking/cancellable_booking.udl +19 -0
  65. package/examples/captured_payment/captured_payment.hsx +0 -4
  66. package/examples/captured_payment/captured_payment.udl +6 -0
  67. package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -2
  68. package/examples/conditional_disbursement/conditional_disbursement.udl +4 -0
  69. package/examples/cost-table.json +136 -8
  70. package/examples/credit_facility/credit_facility.hsx +0 -3
  71. package/examples/credit_facility/credit_facility.udl +17 -1
  72. package/examples/held_payment/held_payment.udl +16 -0
  73. package/examples/instant_transfer/instant_transfer.udl +6 -0
  74. package/examples/metered/metered.udl +5 -1
  75. package/examples/pooled_split/pooled_split.udl +4 -0
  76. package/examples/premium_forward/premium_forward.udl +4 -0
  77. package/examples/reconciled_payout/reconciled_payout.udl +7 -0
  78. package/examples/rotating_pool/rotating_pool.udl +8 -0
  79. package/examples/scheduled/scheduled.udl +18 -3
  80. package/examples/security_deposit/security_deposit.udl +13 -0
  81. package/examples/settlement_batch/settlement_batch.udl +4 -0
  82. package/examples/swap/swap.udl +10 -0
  83. package/examples/threshold_pool/threshold_pool.udl +11 -0
  84. package/examples/weighted_distribution/weighted_distribution.udl +9 -0
  85. package/package.json +10 -10
  86. package/skills/hsx/SKILL.md +2 -36
  87. package/src/cli.ts +41 -5
  88. package/src/compile.ts +14 -6
  89. package/src/cost.ts +13 -16
  90. package/src/parse.ts +15 -10
  91. package/src/std-bundle.ts +21 -88
  92. package/src/typecheck.ts +207 -34
  93. package/src/version.ts +1 -1
  94. package/std/SEMANTICS.md +33 -128
  95. package/std/money_flows/advance.hsx +39 -26
  96. package/std/money_flows/cancellable_booking.hsx +275 -8
  97. package/std/money_flows/captured_payment.hsx +1 -9
  98. package/std/money_flows/conditional_disbursement.hsx +0 -6
  99. package/std/money_flows/credit_facility.hsx +0 -9
  100. package/std/money_flows/held_payment.hsx +39 -1
  101. package/std/money_flows/index.hsx +2 -1
  102. package/std/money_flows/metered.hsx +1 -5
  103. package/std/money_flows/scheduled.hsx +29 -119
  104. package/std/money_flows/threshold_pool.hsx +40 -5
  105. package/std/money_flows/weighted_distribution.hsx +47 -5
  106. package/docs/reference/std/recurring_collection.md +0 -25
  107. package/examples/recurring_collection/README.md +0 -3
  108. package/examples/recurring_collection/recurring_collection.hsx +0 -21
  109. package/examples/recurring_collection/recurring_collection.udl +0 -1135
  110. package/std/money_flows/recurring_collection.hsx +0 -72
@@ -42,6 +42,8 @@ module std.money_flows.held_payment
42
42
  // A zero charge keeps the flow with a zero fee.
43
43
  // - `cancel_offer_life`: ISO 8601 duration a cancellation quote stays open, required with `cancel_charge_bps`.
44
44
  //
45
+ // - `private_actions`: Suppress automatic aliases. Publish chosen actions with `expose`.
46
+ //
45
47
  // ### Decision ports
46
48
  // - `release`: Port deciding release authorization, answered by allowed parties declared in the port.
47
49
  //
@@ -61,7 +63,7 @@ module std.money_flows.held_payment
61
63
  // }
62
64
  // port confirm_delivery { allowed: [buyer] }
63
65
  // ```
64
- 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>, cancel_charge_bps: optional<integer>, cancel_offer_life: optional<text>) {
66
+ 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>, cancel_charge_bps: optional<integer>, cancel_offer_life: optional<text>, private_actions: optional<boolean>) {
65
67
  let(release_name): release;
66
68
  let(release_actor_text): if_eq(len(release_allowed), 1, words(at(release_allowed, 1)), concat("a party allowed by ", release));
67
69
  let(payer_account_field): camel(concat(payer, "_account_id"));
@@ -300,6 +302,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
300
302
  when(payee_fee) {
301
303
  when(on_cancel) {
302
304
  action fund_piece_2 {
305
+ when(private_actions) { public: none; }
303
306
  let(metadata_instrument): "metadata.instrumentId";
304
307
  let(metadata_instance): "metadata.instrumentInstanceId";
305
308
  let(metadata_phase): "metadata.phase";
@@ -310,6 +313,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
310
313
  steps: [];
311
314
  }
312
315
  action fund_piece_3 {
316
+ when(private_actions) { public: none; }
313
317
  let(metadata_instrument): "metadata.instrumentId";
314
318
  let(metadata_instance): "metadata.instrumentInstanceId";
315
319
  let(metadata_phase): "metadata.phase";
@@ -321,6 +325,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
321
325
  }
322
326
  when_eq(payer_fee_kind, percent) {
323
327
  action collect_service_fee {
328
+ when(private_actions) { public: none; }
324
329
  let(metadata_instrument): "metadata.instrumentId";
325
330
  let(metadata_instance): "metadata.instrumentInstanceId";
326
331
  let(metadata_phase): "metadata.phase";
@@ -331,6 +336,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
331
336
  }
332
337
  }
333
338
  action release_piece_2 {
339
+ when(private_actions) { public: none; }
334
340
  let(metadata_instrument): "metadata.instrumentId";
335
341
  let(metadata_instance): "metadata.instrumentInstanceId";
336
342
  let(metadata_phase): "metadata.phase";
@@ -342,6 +348,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
342
348
  steps: [];
343
349
  }
344
350
  action release_piece_3 {
351
+ when(private_actions) { public: none; }
345
352
  let(metadata_instrument): "metadata.instrumentId";
346
353
  let(metadata_instance): "metadata.instrumentInstanceId";
347
354
  let(metadata_phase): "metadata.phase";
@@ -351,6 +358,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
351
358
  steps: [];
352
359
  }
353
360
  action refund_piece_2 {
361
+ when(private_actions) { public: none; }
354
362
  let(metadata_instrument): "metadata.instrumentId";
355
363
  let(metadata_instance): "metadata.instrumentInstanceId";
356
364
  let(metadata_phase): "metadata.phase";
@@ -362,6 +370,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
362
370
  steps: [];
363
371
  }
364
372
  action refund_piece_3 {
373
+ when(private_actions) { public: none; }
365
374
  let(metadata_instrument): "metadata.instrumentId";
366
375
  let(metadata_instance): "metadata.instrumentInstanceId";
367
376
  let(metadata_phase): "metadata.phase";
@@ -371,6 +380,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
371
380
  steps: [];
372
381
  }
373
382
  action unfund_piece_1 {
383
+ when(private_actions) { public: none; }
374
384
  let(metadata_instrument): "metadata.instrumentId";
375
385
  let(metadata_instance): "metadata.instrumentInstanceId";
376
386
  let(metadata_phase): "metadata.phase";
@@ -380,6 +390,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
380
390
  steps: [];
381
391
  }
382
392
  action unfund_piece_2 {
393
+ when(private_actions) { public: none; }
383
394
  let(metadata_instrument): "metadata.instrumentId";
384
395
  let(metadata_instance): "metadata.instrumentInstanceId";
385
396
  let(metadata_phase): "metadata.phase";
@@ -390,6 +401,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
390
401
  }
391
402
  when_eq(payer_fee_kind, percent) {
392
403
  action unfund_piece_3 {
404
+ when(private_actions) { public: none; }
393
405
  let(metadata_instrument): "metadata.instrumentId";
394
406
  let(metadata_instance): "metadata.instrumentInstanceId";
395
407
  let(metadata_phase): "metadata.phase";
@@ -430,6 +442,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
430
442
  computes derived: { field: derived_field; source_field: derived_source; rounding: floor; rule: { kind: percentage_of; bps: derived_rule; }; };
431
443
  }
432
444
  action create {
445
+ when(private_actions) { public: none; }
433
446
  let(owner_id): "owner.id";
434
447
  let(owner_type): "owner.type";
435
448
  agent_description: concat("Open the ", words(instrument), " and provision the escrow account that holds the funds. This is the first call, and every funding and release action moves money through that account. It does not move the held amount itself.");
@@ -444,6 +457,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
444
457
  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; }; }];
445
458
  }
446
459
  action fund_piece_1 {
460
+ when(private_actions) { public: none; }
447
461
  let(metadata_instrument): "metadata.instrumentId";
448
462
  let(metadata_instance): "metadata.instrumentInstanceId";
449
463
  let(metadata_phase): "metadata.phase";
@@ -465,6 +479,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
465
479
  steps: [];
466
480
  }
467
481
  action [release_name] {
482
+ when(private_actions) { public: none; }
468
483
  let(metadata_instrument): "metadata.instrumentId";
469
484
  let(metadata_instance): "metadata.instrumentInstanceId";
470
485
  let(metadata_phase): "metadata.phase";
@@ -498,6 +513,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
498
513
  }
499
514
  when(release_deadline) {
500
515
  action release_on_deadline {
516
+ when(private_actions) { public: none; }
501
517
  let(metadata_instrument): "metadata.instrumentId";
502
518
  let(metadata_instance): "metadata.instrumentInstanceId";
503
519
  let(metadata_phase): "metadata.phase";
@@ -514,6 +530,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
514
530
  }
515
531
  when(on_cancel) {
516
532
  action cancel {
533
+ when(private_actions) { public: none; }
517
534
  let(metadata_instrument): "metadata.instrumentId";
518
535
  let(metadata_instance): "metadata.instrumentInstanceId";
519
536
  let(metadata_phase): "metadata.phase";
@@ -530,6 +547,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
530
547
  }
531
548
  when(has_cancel_quote) {
532
549
  action quote_cancellation {
550
+ when(private_actions) { public: none; }
533
551
  agent_description: concat("Price the cancellation and hold that price open. No money moves. The charge the ", words(payee), " keeps and the refund the ", words(payer), " gets back are worked out here and frozen for ", cancel_offer_life, ", after which a fresh call prices it again. Call cancel to spend the quote.");
534
552
  summary: "Price the cancellation and hold that price open";
535
553
  when(release_deadline) { deadline { field: release_deadline; }; }
@@ -546,6 +564,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
546
564
  steps: [];
547
565
  }
548
566
  action cancel {
567
+ when(private_actions) { public: none; }
549
568
  let(metadata_instrument): "metadata.instrumentId";
550
569
  let(metadata_instance): "metadata.instrumentInstanceId";
551
570
  let(metadata_phase): "metadata.phase";
@@ -557,6 +576,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
557
576
  steps: [];
558
577
  }
559
578
  action retain_cancellation_charge {
579
+ when(private_actions) { public: none; }
560
580
  let(metadata_instrument): "metadata.instrumentId";
561
581
  let(metadata_instance): "metadata.instrumentInstanceId";
562
582
  let(metadata_phase): "metadata.phase";
@@ -574,6 +594,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
574
594
  when(payee_fee) {
575
595
  when_not(on_cancel) {
576
596
  action fund_piece_2 {
597
+ when(private_actions) { public: none; }
577
598
  let(metadata_instrument): "metadata.instrumentId";
578
599
  let(metadata_instance): "metadata.instrumentInstanceId";
579
600
  let(metadata_phase): "metadata.phase";
@@ -585,6 +606,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
585
606
  steps: [];
586
607
  }
587
608
  action release_piece_2 {
609
+ when(private_actions) { public: none; }
588
610
  let(metadata_instrument): "metadata.instrumentId";
589
611
  let(metadata_instance): "metadata.instrumentInstanceId";
590
612
  let(metadata_phase): "metadata.phase";
@@ -595,6 +617,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
595
617
  steps: [];
596
618
  }
597
619
  action unfund_piece_1 {
620
+ when(private_actions) { public: none; }
598
621
  let(metadata_instrument): "metadata.instrumentId";
599
622
  let(metadata_instance): "metadata.instrumentInstanceId";
600
623
  let(metadata_phase): "metadata.phase";
@@ -608,6 +631,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
608
631
  when_not(payee_fee) {
609
632
  when(cancel_splits) {
610
633
  action fund_piece_2 {
634
+ when(private_actions) { public: none; }
611
635
  let(metadata_instrument): "metadata.instrumentId";
612
636
  let(metadata_instance): "metadata.instrumentInstanceId";
613
637
  let(metadata_phase): "metadata.phase";
@@ -618,6 +642,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
618
642
  steps: [];
619
643
  }
620
644
  action release_piece_2 {
645
+ when(private_actions) { public: none; }
621
646
  let(metadata_instrument): "metadata.instrumentId";
622
647
  let(metadata_instance): "metadata.instrumentInstanceId";
623
648
  let(metadata_phase): "metadata.phase";
@@ -627,6 +652,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
627
652
  steps: [];
628
653
  }
629
654
  action refund_piece_2 {
655
+ when(private_actions) { public: none; }
630
656
  let(metadata_instrument): "metadata.instrumentId";
631
657
  let(metadata_instance): "metadata.instrumentInstanceId";
632
658
  let(metadata_phase): "metadata.phase";
@@ -636,6 +662,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
636
662
  steps: [];
637
663
  }
638
664
  action unfund_piece_1 {
665
+ when(private_actions) { public: none; }
639
666
  let(metadata_instrument): "metadata.instrumentId";
640
667
  let(metadata_instance): "metadata.instrumentInstanceId";
641
668
  let(metadata_phase): "metadata.phase";
@@ -647,6 +674,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
647
674
  }
648
675
  }
649
676
  action abandon {
677
+ when(private_actions) { public: none; }
650
678
  agent_description: concat("Close a ", words(instrument), " that was never funded. The escrow account must be empty, so this refuses once any piece is in it. It moves no money and the settlement ends here with no way back.");
651
679
  summary: "Abandon the settlement before any money is held";
652
680
  requires drained: { path: refs.escrowAccountId; };
@@ -654,6 +682,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
654
682
  steps: [];
655
683
  }
656
684
  action dispute {
685
+ when(private_actions) { public: none; }
657
686
  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.");
658
687
  description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
659
688
  summary: concat("Dispute a ", words(instrument), " settlement");
@@ -662,6 +691,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
662
691
  steps: [];
663
692
  }
664
693
  action resume {
694
+ when(private_actions) { public: none; }
665
695
  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.");
666
696
  description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
667
697
  summary: concat("Resume a ", words(instrument), " settlement");
@@ -707,6 +737,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
707
737
  parties { beneficiary: payee_account_field; payer: payer_account_field; }
708
738
  required: [payer_account_field, payee_account_field, platformAccountId, amount, whole_fee_field, currency, release_deadline];
709
739
  action create {
740
+ when(private_actions) { public: none; }
710
741
  let(owner_id): "owner.id";
711
742
  let(owner_type): "owner.type";
712
743
  agent_description: concat("Open the ", words(instrument), " and provision the escrow account that holds the money. No money moves on this call. ", release_deadline, " is fixed here and never moves again, so one date governs the early release, the cancellation, and the automatic payout.");
@@ -715,6 +746,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
715
746
  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; }; }];
716
747
  }
717
748
  action fund {
749
+ when(private_actions) { public: none; }
718
750
  agent_description: concat("Move ", amount, " from the ", words(payer), " into escrow and charge ", whole_fee_field, " on top to the platform account, as one linked batch. This moves money and the ", words(payer), " pays both. Only a created hold funds. The fee never enters escrow, so it is non-refundable however the hold ends.");
719
751
  summary: "Fund the hold and collect the custody fee";
720
752
  moves: [
@@ -725,6 +757,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
725
757
  steps: [];
726
758
  }
727
759
  action [release_action] {
760
+ when(private_actions) { public: none; }
728
761
  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.");
729
762
  summary: concat("Release the hold to the ", words(payee), " early");
730
763
  deadline: { field: release_deadline; };
@@ -734,6 +767,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
734
767
  steps: [];
735
768
  }
736
769
  action release_on_deadline {
770
+ when(private_actions) { public: none; }
737
771
  summary: concat("Release the hold to the ", words(payee), " on its release date");
738
772
  due: { field: release_deadline; };
739
773
  event_name: concat(instrument, ".released_on_deadline");
@@ -741,6 +775,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
741
775
  steps: [];
742
776
  }
743
777
  action cancel {
778
+ when(private_actions) { public: none; }
744
779
  agent_description: concat("Return the whole ", amount, " from escrow to the ", words(payer), " and end the hold. This moves money and does not reverse. Only a funded hold cancels, and only before ", release_deadline, " passes. The custody fee stays posted because the custody it paid for was already provided.");
745
780
  summary: "Cancel the hold and return the held amount";
746
781
  deadline: { field: release_deadline; };
@@ -749,6 +784,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
749
784
  steps: [];
750
785
  }
751
786
  action abandon {
787
+ when(private_actions) { public: none; }
752
788
  agent_description: concat("Close a ", words(instrument), " that was opened and never funded. No money has moved and the escrow must be empty, so this is a lifecycle transition only. A funded hold refuses. Release it to the ", words(payee), " or cancel it back to the ", words(payer), " instead.");
753
789
  summary: "Abandon the hold before it is funded";
754
790
  moves: [];
@@ -756,6 +792,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
756
792
  steps: [];
757
793
  }
758
794
  action dispute {
795
+ when(private_actions) { public: none; }
759
796
  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.");
760
797
  description: concat("Freezes funded ", words(instrument), ", preserving the entire balance while the parties decide what happens next.");
761
798
  summary: concat("Dispute a ", words(instrument), " settlement");
@@ -764,6 +801,7 @@ export instrument held_payment<C>(payer: party, payee: party, amount: money<C>,
764
801
  steps: [];
765
802
  }
766
803
  action resume {
804
+ when(private_actions) { public: none; }
767
805
  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.");
768
806
  description: concat("Ends the dispute hold without moving money and returns the fully funded ", words(instrument), " to its first milestone.");
769
807
  summary: concat("Resume a ", words(instrument), " settlement");
@@ -1,3 +1,4 @@
1
1
  module std.money_flows
2
2
 
3
- // Money flows are imported by their full std.money_flows.<name> path.
3
+ // Import named exports from "std/money_flows".
4
+ // SEMANTICS.md groups modules by lifecycle and explains their money effects.
@@ -11,10 +11,6 @@ module std.money_flows.metered
11
11
  // - vs `scheduled`: `metered` charges variable amounts per event calculated from consumed units and committed
12
12
  // rate card prices. `scheduled` executes transfers on fixed calendar recurrence intervals (`every`, `first_due`)
13
13
  // with predetermined amounts or installments.
14
- // - vs `recurring_collection`: `metered` calculates and executes real ledger transfers for each usage charge.
15
- // `recurring_collection` is a lightweight status tracker for collection runs without balance transfers or rate cards.
16
- // - vs `instant_transfer`: `instant_transfer` executes a single immediate payment. `metered` manages a stateful
17
- // open billing cycle accepting multiple usage charges under a fixed rate card.
18
14
  //
19
15
  // ### Parameters
20
16
  // - `payer`: The customer being billed for usage.
@@ -49,7 +45,7 @@ export instrument metered(
49
45
  ) {
50
46
  let(instrument_words): words(instrument);
51
47
  title: sentence(instrument);
52
- agent_description: "Reach for metered when the {payer} is billed per unit at a rate card fixed when the period opens, and the period closes on {close_by}. Every charge is its own transfer. Pick recurring collection for a flat repeating amount and instant transfer for a one-off payment.";
48
+ agent_description: "Reach for metered when the {payer} is billed per unit at a rate card fixed when the period opens, and the period closes on {close_by}. Every charge is its own transfer. Pick scheduled for a flat repeating amount and instant transfer for a one-off payment.";
53
49
  summary: "Metered charges from {payer} on a committed rate card";
54
50
  description: "Metered usage: the {payer} is charged per unit at the committed rate card until the period closes on its stored end date";
55
51
 
@@ -5,6 +5,7 @@ module std.money_flows.scheduled
5
5
  // ### Purpose
6
6
  // `scheduled` automates time-anchored payment series between one payer and one payee.
7
7
  // It supports three modes:
8
+ //
8
9
  // 1. Installment plan: Partitions a fixed total amount into `count` installments, each collected on its own stored date.
9
10
  // 2. Open recurring subscription: Charges a recurring amount on an interval `every` until an `until` port fires.
10
11
  // 3. Debt obligation: Manages legally binding multi-installment debt with delinquency tracking, mandate evidence, and child payment records.
@@ -12,8 +13,6 @@ module std.money_flows.scheduled
12
13
  // ### Selection guidance
13
14
  // - vs `metered`: `scheduled` executes calendar-based recurring charges or fixed installment plans.
14
15
  // `metered` bills variable usage per event based on a committed rate card.
15
- // - vs `recurring_collection`: `scheduled` actively moves ledger balances and partitions amounts.
16
- // `recurring_collection` is a lightweight status tracker for collection runs without balance transfers.
17
16
  // - vs `rotating_pool`: `scheduled` coordinates one payer to one payee on a calendar.
18
17
  // `rotating_pool` coordinates a multi-party peer circle where members rotate turns receiving the entire pot.
19
18
  //
@@ -73,8 +72,6 @@ export instrument scheduled<C>(
73
72
  agent_description: "Reach for scheduled when one payer owes one payee on a calendar. It covers a plan of fixed installments partitioning one total, an obligation whose installments are paid through child payment records and can go delinquent, and an open period charge that repeats until a cancel port fires. Rotating pool is for a group taking turns.";
74
73
  when_not(mode) {
75
74
  let(instrument_words): words(instrument);
76
- let(first_percent): if_eq(count, 3, "33.34%", if_eq(count, 2, "50%", "25%"));
77
- let(other_percent): if_eq(count, 3, "33.33%", if_eq(count, 2, "50%", "25%"));
78
75
 
79
76
  title: sentence(instrument);
80
77
  summary: "{count}-installment schedule from {payer} to {payee}";
@@ -91,11 +88,10 @@ export instrument scheduled<C>(
91
88
  description: "Due date of the first installment; installment k falls {every} after its predecessor";
92
89
  }
93
90
  for i in count {
94
- let(piece_percent): if_eq(i, 1, first_percent, other_percent);
95
91
  let(remainder_words): if_eq(i, 1, " (carries the integer-division remainder)", "");
96
92
  installment[i]Amount: {
97
93
  type: money<C>;
98
- description: "Installment {i} of {count}{remainder_words}: about {piece_percent} of {amount}, collected on its own stored-date anchor";
94
+ description: "Installment {i} of {count}{remainder_words}: a stored share of {amount}, collected on its own stored-date anchor";
99
95
  }
100
96
  }
101
97
  }
@@ -153,13 +149,7 @@ export instrument scheduled<C>(
153
149
  let(parent_ref_field): camel(concat(instrument, "_id"));
154
150
  let(has_advance): advance_to;
155
151
  let(same_debtor): if_eq(payer, debtor, true, false);
156
- let(parent_statuses): if_eq(count, 2,
157
- ["active", "installment_1_delinquent", "installment_2_delinquent"],
158
- if_eq(count, 3,
159
- ["active", "installment_1_delinquent", "installment_2_delinquent", "installment_3_delinquent"],
160
- ["active", "installment_1_delinquent", "installment_2_delinquent", "installment_3_delinquent", "installment_4_delinquent"]
161
- )
162
- );
152
+ let(parent_statuses): concat_lists(["active"], names("installment_", count, "_delinquent"));
163
153
  let(delinquent_statuses): names("installment_", count, "_delinquent");
164
154
  let(last_offset): scale_duration(every, sub(count, 1));
165
155
  let(reference_filter_key): "x-hyperscale-reference-filter";
@@ -196,51 +186,13 @@ export instrument scheduled<C>(
196
186
  parent_field: concat("installment", i, "Amount");
197
187
  };
198
188
  }
199
- when(has_advance) {
200
- when_not(same_debtor) {
201
- required: if_eq(count, 2,
202
- [advance_field, payee_field, payer_field, debtor_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount"],
203
- if_eq(count, 3,
204
- [advance_field, payee_field, payer_field, debtor_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount"],
205
- [advance_field, payee_field, payer_field, debtor_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount", "installment4Amount"]
206
- )
207
- );
208
- }
209
- when(same_debtor) {
210
- when_not(derived_amount) {
211
- required: if_eq(count, 3,
212
- [advance_field, payee_field, payer_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount"],
213
- [advance_field, payee_field, payer_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount", "installment4Amount"]
214
- );
215
- }
216
- when(derived_amount) {
217
- required: if_eq(count, 2,
218
- [advance_field, payee_field, payer_field, "platformAccountId", "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount"],
219
- [advance_field, payee_field, payer_field, "platformAccountId", "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount", "installment4Amount"]
220
- );
221
- }
222
- }
223
- }
224
- when_not(has_advance) {
225
- when_not(same_debtor) {
226
- required: if_eq(count, 2,
227
- [payee_field, payer_field, debtor_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount"],
228
- if_eq(count, 3,
229
- [payee_field, payer_field, debtor_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount"],
230
- [payee_field, payer_field, debtor_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount", "installment4Amount"]
231
- )
232
- );
233
- }
234
- when(same_debtor) {
235
- required: if_eq(count, 2,
236
- [payee_field, payer_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount"],
237
- if_eq(count, 3,
238
- [payee_field, payer_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount"],
239
- [payee_field, payer_field, "currency", amount_field, first_due_field, "installment1Amount", "installment2Amount", "installment3Amount", "installment4Amount"]
240
- )
241
- );
242
- }
243
- }
189
+ required: concat_lists(
190
+ [payee_field, payer_field, "currency", amount_field, first_due_field],
191
+ names("installment", count, "Amount"),
192
+ if_eq(has_advance, false, [], [advance_field]),
193
+ if_eq(same_debtor, true, [], [debtor_field]),
194
+ if_eq(derived_amount, false, [], ["platformAccountId"])
195
+ );
244
196
 
245
197
  fields {
246
198
  when(has_advance) {
@@ -315,68 +267,26 @@ export instrument scheduled<C>(
315
267
  when_not(same_debtor) { payer: payer; }
316
268
  }
317
269
 
318
- let(count_is_2): if_eq(count, 2, true, false);
319
- let(count_is_3): if_eq(count, 3, true, false);
320
- let(count_is_4): if_eq(count, 4, true, false);
321
- when(count_is_2) {
322
- lifecycle {
323
- states draft;
324
- when(has_advance) { states approved; }
325
- states active installment_1_delinquent installment_2_delinquent written_off repaid;
326
- initial draft;
327
- when(has_advance) { on approve: draft -> approved; }
328
- when_not(has_advance) { on approve: draft -> active; }
329
- when(has_advance) { on advance: approved -> active; }
330
- on collect_installment_1: installment_1_delinquent|installment_2_delinquent -> active;
331
- on collect_installment_2: installment_1_delinquent|installment_2_delinquent -> active;
332
- on mark_installment_1_delinquent: active|installment_2_delinquent -> installment_1_delinquent;
333
- on mark_installment_2_delinquent: active|installment_1_delinquent -> installment_2_delinquent;
334
- on complete: active|installment_1_delinquent|installment_2_delinquent -> repaid;
335
- when(has_advance) { on write_off: draft|approved|active|installment_1_delinquent|installment_2_delinquent -> written_off; }
336
- when_not(has_advance) { on write_off: draft|active|installment_1_delinquent|installment_2_delinquent -> written_off; }
337
- }
338
- }
339
- when(count_is_3) {
340
- lifecycle {
341
- states draft;
342
- when(has_advance) { states approved; }
343
- states active installment_1_delinquent installment_2_delinquent installment_3_delinquent written_off repaid;
344
- initial draft;
345
- when(has_advance) { on approve: draft -> approved; }
346
- when_not(has_advance) { on approve: draft -> active; }
347
- when(has_advance) { on advance: approved -> active; }
348
- on collect_installment_1: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> active;
349
- on collect_installment_2: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> active;
350
- on collect_installment_3: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> active;
351
- on mark_installment_1_delinquent: active|installment_2_delinquent|installment_3_delinquent -> installment_1_delinquent;
352
- on mark_installment_2_delinquent: active|installment_1_delinquent|installment_3_delinquent -> installment_2_delinquent;
353
- on mark_installment_3_delinquent: active|installment_1_delinquent|installment_2_delinquent -> installment_3_delinquent;
354
- on complete: active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> repaid;
355
- when(has_advance) { on write_off: draft|approved|active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> written_off; }
356
- when_not(has_advance) { on write_off: draft|active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> written_off; }
357
- }
358
- }
359
- when(count_is_4) {
360
- lifecycle {
361
- states draft;
362
- when(has_advance) { states approved; }
363
- states active installment_1_delinquent installment_2_delinquent installment_3_delinquent installment_4_delinquent written_off repaid;
364
- initial draft;
365
- when(has_advance) { on approve: draft -> approved; }
366
- when_not(has_advance) { on approve: draft -> active; }
367
- when(has_advance) { on advance: approved -> active; }
368
- on collect_installment_1: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> active;
369
- on collect_installment_2: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> active;
370
- on collect_installment_3: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> active;
371
- on collect_installment_4: installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> active;
372
- on mark_installment_1_delinquent: active|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> installment_1_delinquent;
373
- on mark_installment_2_delinquent: active|installment_1_delinquent|installment_3_delinquent|installment_4_delinquent -> installment_2_delinquent;
374
- on mark_installment_3_delinquent: active|installment_1_delinquent|installment_2_delinquent|installment_4_delinquent -> installment_3_delinquent;
375
- on mark_installment_4_delinquent: active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent -> installment_4_delinquent;
376
- on complete: active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> repaid;
377
- when(has_advance) { on write_off: draft|approved|active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> written_off; }
378
- when_not(has_advance) { on write_off: draft|active|installment_1_delinquent|installment_2_delinquent|installment_3_delinquent|installment_4_delinquent -> written_off; }
270
+ lifecycle {
271
+ states draft;
272
+ when(has_advance) { states approved; }
273
+ states active;
274
+ let(states_key): "states";
275
+ [states_key]: delinquent_statuses;
276
+ states written_off repaid;
277
+ initial draft;
278
+ when(has_advance) { on approve: draft -> approved; on advance: approved -> active; }
279
+ when_not(has_advance) { on approve: draft -> active; }
280
+ for i in count {
281
+ let(target): concat("installment_", i, "_delinquent");
282
+ let(other_states): without(parent_statuses, target);
283
+ on collect_installment_[i]: delinquent_statuses -> active;
284
+ let(mark_action): concat("mark_installment_", i, "_delinquent");
285
+ on [mark_action]: other_states -> target;
379
286
  }
287
+ on complete: parent_statuses -> repaid;
288
+ when(has_advance) { on write_off: draft|approved|parent_statuses -> written_off; }
289
+ when_not(has_advance) { on write_off: draft|parent_statuses -> written_off; }
380
290
  }
381
291
 
382
292
  action create {
@@ -14,7 +14,7 @@ module std.money_flows.threshold_pool
14
14
  // `weighted_distribution` pays funds outward from one source pool to many weighted recipients.
15
15
  //
16
16
  // ### Parameters
17
- // - `contributor`: The archetype party representing individual contributors.
17
+ // - `contributor`: The party representing individual contributors.
18
18
  // - `beneficiary`: The beneficiary party receiving the settled pool if the target is met.
19
19
  // - `target`: Total target funding threshold in minor units of currency `C`.
20
20
  // - `commitment`: Binding name for individual contributor commitment amounts.
@@ -51,6 +51,13 @@ module std.money_flows.threshold_pool
51
51
  // fail_policy: whole_commitment_refund
52
52
  // }
53
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
+ //
54
61
  export instrument threshold_pool<C>(
55
62
  contributor: optional<party>,
56
63
  beneficiary: optional<party>,
@@ -66,6 +73,7 @@ export instrument threshold_pool<C>(
66
73
  memo: optional<text>,
67
74
  contribution_instrument: optional<block>,
68
75
  wording: optional<block>,
76
+ funding_join: optional<block>,
69
77
  ) {
70
78
  when(wording) {
71
79
  let(wording_unknown_keys): keys_except(wording, "pool_summary", "pool_description", "create_summary", "commitment_create_summary", "commitment_cancel_summary", "commitment_refund_summary");
@@ -217,6 +225,7 @@ export instrument threshold_pool<C>(
217
225
  let(beneficiary_account_field): camel(beneficiary_account);
218
226
  let(target_field): camel(target);
219
227
  let(close_field): camel(close_by);
228
+ let(obligation_type): get(funding_join, "obligation");
220
229
 
221
230
  fields {
222
231
  [beneficiary_account_field] { type: account<C>; description: beneficiary_account_description; "x-hyperscale-reference-filter": { column: role; values: [customer_balance]; }; }
@@ -224,6 +233,18 @@ export instrument threshold_pool<C>(
224
233
  currency { type: text; description: currency_description; maxLength: 3; minLength: 3; pattern: "^[A-Z]{3}$"; }
225
234
  when(memo) { [memo] { type: text; optional: true; maxLength: 180; minLength: 1; } }
226
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; };
227
248
  }
228
249
  lifecycle {
229
250
  states open active failed settled;
@@ -233,14 +254,15 @@ export instrument threshold_pool<C>(
233
254
  on fail: open -> failed;
234
255
  }
235
256
  parties { beneficiary: beneficiary_account_field; }
236
- required: [beneficiary_account_field, target_field, currency, close_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]; }
237
259
  aggregate_invariants { childField: contribution_amount_field; childInstrumentId: contribution_id; childRefField: contribution_ref_field; childStatuses: [committed_status, collected_status]; parentField: target_field; }
238
260
  caller_parked_states { active: active_parked; }
239
261
  id_prefix: instrument_id_prefix;
240
262
  action create {
241
263
  public: none;
242
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.";
243
- examples: create_examples;
265
+ when_not(funding_join) { examples: create_examples; }
244
266
  moves: [];
245
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; }; }];
246
268
  }
@@ -263,8 +285,21 @@ export instrument threshold_pool<C>(
263
285
  action close {
264
286
  public: none;
265
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.";
266
- examples: close_examples;
267
- requires aggregate { instrumentId: contribution_id; over: children; refField: contribution_ref_field; statuses: [collected_status]; check: { kind: all_in; }; }
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
+ }
268
303
  moves: [];
269
304
  steps: [];
270
305
  }