@hyperscale0/hsx 1.0.0-beta.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. package/CHANGELOG.md +58 -9
  2. package/LICENSING.md +3 -3
  3. package/README.md +39 -149
  4. package/TRADEMARKS.md +4 -4
  5. package/bin/hsx.ts +2 -0
  6. package/dist/bin/hsx.js +2 -0
  7. package/dist/bin/hsx.js.map +1 -1
  8. package/dist/src/ast.d.ts +135 -10
  9. package/dist/src/ast.d.ts.map +1 -1
  10. package/dist/src/ast.js +24 -0
  11. package/dist/src/ast.js.map +1 -1
  12. package/dist/src/cli.d.ts +4 -1
  13. package/dist/src/cli.d.ts.map +1 -1
  14. package/dist/src/cli.js +172 -16
  15. package/dist/src/cli.js.map +1 -1
  16. package/dist/src/compile.d.ts +42 -7
  17. package/dist/src/compile.d.ts.map +1 -1
  18. package/dist/src/compile.js +82 -16
  19. package/dist/src/compile.js.map +1 -1
  20. package/dist/src/cost.d.ts +78 -0
  21. package/dist/src/cost.d.ts.map +1 -0
  22. package/dist/src/cost.js +369 -0
  23. package/dist/src/cost.js.map +1 -0
  24. package/dist/src/diagnostics.d.ts +11 -0
  25. package/dist/src/diagnostics.d.ts.map +1 -0
  26. package/dist/src/diagnostics.js +420 -0
  27. package/dist/src/diagnostics.js.map +1 -0
  28. package/dist/src/emit.d.ts +34 -0
  29. package/dist/src/emit.d.ts.map +1 -0
  30. package/dist/src/emit.js +137 -0
  31. package/dist/src/emit.js.map +1 -0
  32. package/dist/src/entry-overrides.d.ts +17 -3
  33. package/dist/src/entry-overrides.d.ts.map +1 -1
  34. package/dist/src/entry-overrides.js +180 -35
  35. package/dist/src/entry-overrides.js.map +1 -1
  36. package/dist/src/format.d.ts +10 -0
  37. package/dist/src/format.d.ts.map +1 -0
  38. package/dist/src/format.js +183 -0
  39. package/dist/src/format.js.map +1 -0
  40. package/dist/src/index.d.ts +11 -4
  41. package/dist/src/index.d.ts.map +1 -1
  42. package/dist/src/index.js +10 -3
  43. package/dist/src/index.js.map +1 -1
  44. package/dist/src/ir.d.ts +66 -0
  45. package/dist/src/ir.d.ts.map +1 -0
  46. package/dist/src/ir.js +15 -0
  47. package/dist/src/ir.js.map +1 -0
  48. package/dist/src/lex.d.ts +9 -1
  49. package/dist/src/lex.d.ts.map +1 -1
  50. package/dist/src/lex.js +50 -4
  51. package/dist/src/lex.js.map +1 -1
  52. package/dist/src/limits.d.ts +2 -3
  53. package/dist/src/limits.d.ts.map +1 -1
  54. package/dist/src/limits.js +2 -3
  55. package/dist/src/limits.js.map +1 -1
  56. package/dist/src/lsp/server.d.ts +32 -0
  57. package/dist/src/lsp/server.d.ts.map +1 -0
  58. package/dist/src/lsp/server.js +391 -0
  59. package/dist/src/lsp/server.js.map +1 -0
  60. package/dist/src/modules.d.ts +38 -0
  61. package/dist/src/modules.d.ts.map +1 -0
  62. package/dist/src/modules.js +368 -0
  63. package/dist/src/modules.js.map +1 -0
  64. package/dist/src/parse.d.ts.map +1 -1
  65. package/dist/src/parse.js +590 -17
  66. package/dist/src/parse.js.map +1 -1
  67. package/dist/src/std-bundle.d.ts +2 -0
  68. package/dist/src/std-bundle.d.ts.map +1 -0
  69. package/dist/src/std-bundle.js +88 -0
  70. package/dist/src/std-bundle.js.map +1 -0
  71. package/dist/src/std-library.d.ts +5 -0
  72. package/dist/src/std-library.d.ts.map +1 -0
  73. package/dist/src/std-library.js +9 -0
  74. package/dist/src/std-library.js.map +1 -0
  75. package/dist/src/typecheck.d.ts +8 -0
  76. package/dist/src/typecheck.d.ts.map +1 -0
  77. package/dist/src/typecheck.js +2958 -0
  78. package/dist/src/typecheck.js.map +1 -0
  79. package/dist/src/version.d.ts +4 -15
  80. package/dist/src/version.d.ts.map +1 -1
  81. package/dist/src/version.js +4 -15
  82. package/dist/src/version.js.map +1 -1
  83. package/docs/README.md +26 -0
  84. package/docs/guide/01-first-program.md +24 -0
  85. package/docs/guide/02-money.md +25 -0
  86. package/docs/guide/03-instruments.md +22 -0
  87. package/docs/guide/04-lifecycles.md +25 -0
  88. package/docs/guide/05-fees-and-splits.md +45 -0
  89. package/docs/guide/06-schedules.md +22 -0
  90. package/docs/guide/07-composition.md +46 -0
  91. package/docs/guide/08-writing-a-module.md +26 -0
  92. package/docs/guide/09-cost.md +24 -0
  93. package/docs/guide/10-diagnostics.md +27 -0
  94. package/docs/llms-full.txt +1739 -0
  95. package/docs/llms.txt +45 -0
  96. package/docs/playground.md +54 -0
  97. package/docs/reference/cli.md +35 -0
  98. package/docs/reference/diagnostics.md +619 -0
  99. package/docs/reference/grammar.md +39 -0
  100. package/docs/reference/std/advance.md +35 -0
  101. package/docs/reference/std/cancellable_booking.md +35 -0
  102. package/docs/reference/std/captured_payment.md +43 -0
  103. package/docs/reference/std/conditional_disbursement.md +33 -0
  104. package/docs/reference/std/credit_facility.md +35 -0
  105. package/docs/reference/std/held_payment.md +64 -0
  106. package/docs/reference/std/instant_transfer.md +30 -0
  107. package/docs/reference/std/metered.md +27 -0
  108. package/docs/reference/std/pooled_split.md +26 -0
  109. package/docs/reference/std/premium_forward.md +39 -0
  110. package/docs/reference/std/reconciled_payout.md +33 -0
  111. package/docs/reference/std/recurring_collection.md +25 -0
  112. package/docs/reference/std/rotating_pool.md +42 -0
  113. package/docs/reference/std/scheduled.md +51 -0
  114. package/docs/reference/std/security_deposit.md +46 -0
  115. package/docs/reference/std/settlement_batch.md +42 -0
  116. package/docs/reference/std/swap.md +46 -0
  117. package/docs/reference/std/threshold_pool.md +47 -0
  118. package/docs/reference/std/weighted_distribution.md +39 -0
  119. package/docs/reference/types.md +20 -0
  120. package/docs/reference/udl-output.md +13 -0
  121. package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
  122. package/docs/sessions/2026-09-02-two-instruments.md +41 -0
  123. package/examples/01-first-program/README.md +45 -0
  124. package/examples/01-first-program/tip-jar.hsx +17 -0
  125. package/examples/02-imports-and-archetypes/README.md +96 -0
  126. package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
  127. package/examples/03-diagnostics/README.md +68 -0
  128. package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
  129. package/examples/03-diagnostics/corner-shop.hsx +19 -0
  130. package/examples/04-complete-product/README.md +84 -0
  131. package/examples/04-complete-product/study-hall.hsx +67 -0
  132. package/examples/05-watch-club/README.md +9 -0
  133. package/examples/05-watch-club/watch-club.hsx +124 -0
  134. package/examples/README.md +18 -0
  135. package/examples/advance/README.md +3 -0
  136. package/examples/advance/advance.hsx +13 -0
  137. package/examples/advance/advance.udl +339 -0
  138. package/examples/cancellable_booking/README.md +3 -0
  139. package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
  140. package/examples/cancellable_booking/cancellable_booking.udl +391 -0
  141. package/examples/captured_payment/README.md +3 -0
  142. package/examples/captured_payment/captured_payment.hsx +21 -0
  143. package/examples/captured_payment/captured_payment.udl +677 -0
  144. package/examples/conditional_disbursement/README.md +3 -0
  145. package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
  146. package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
  147. package/examples/cost-table.json +386 -0
  148. package/examples/credit_facility/README.md +3 -0
  149. package/examples/credit_facility/credit_facility.hsx +27 -0
  150. package/examples/credit_facility/credit_facility.udl +1373 -0
  151. package/examples/held_payment/README.md +3 -0
  152. package/examples/held_payment/held_payment.hsx +13 -0
  153. package/examples/held_payment/held_payment.udl +507 -0
  154. package/examples/instant_transfer/README.md +3 -0
  155. package/examples/instant_transfer/instant_transfer.hsx +16 -0
  156. package/examples/instant_transfer/instant_transfer.udl +364 -0
  157. package/examples/metered/README.md +3 -0
  158. package/examples/metered/metered.hsx +13 -0
  159. package/examples/metered/metered.udl +247 -0
  160. package/examples/pooled_split/README.md +3 -0
  161. package/examples/pooled_split/pooled_split.hsx +15 -0
  162. package/examples/pooled_split/pooled_split.udl +425 -0
  163. package/examples/premium_forward/README.md +3 -0
  164. package/examples/premium_forward/premium_forward.hsx +19 -0
  165. package/examples/premium_forward/premium_forward.udl +594 -0
  166. package/examples/reconciled_payout/README.md +3 -0
  167. package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
  168. package/examples/reconciled_payout/reconciled_payout.udl +303 -0
  169. package/examples/recurring_collection/README.md +3 -0
  170. package/examples/recurring_collection/recurring_collection.hsx +21 -0
  171. package/examples/recurring_collection/recurring_collection.udl +1130 -0
  172. package/examples/rotating_pool/README.md +3 -0
  173. package/examples/rotating_pool/rotating_pool.hsx +18 -0
  174. package/examples/rotating_pool/rotating_pool.udl +4352 -0
  175. package/examples/scheduled/README.md +3 -0
  176. package/examples/scheduled/scheduled.hsx +12 -0
  177. package/examples/scheduled/scheduled.udl +335 -0
  178. package/examples/security_deposit/README.md +3 -0
  179. package/examples/security_deposit/security_deposit.hsx +21 -0
  180. package/examples/security_deposit/security_deposit.udl +293 -0
  181. package/examples/settlement_batch/README.md +3 -0
  182. package/examples/settlement_batch/settlement_batch.hsx +19 -0
  183. package/examples/settlement_batch/settlement_batch.udl +753 -0
  184. package/examples/swap/README.md +3 -0
  185. package/examples/swap/swap.hsx +19 -0
  186. package/examples/swap/swap.udl +876 -0
  187. package/examples/threshold_pool/README.md +3 -0
  188. package/examples/threshold_pool/threshold_pool.hsx +16 -0
  189. package/examples/threshold_pool/threshold_pool.udl +667 -0
  190. package/examples/weighted_distribution/README.md +3 -0
  191. package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
  192. package/examples/weighted_distribution/weighted_distribution.udl +337 -0
  193. package/package.json +27 -6
  194. package/skills/hsx/SKILL.md +483 -0
  195. package/src/ast.ts +193 -9
  196. package/src/cli.ts +191 -20
  197. package/src/compile.ts +160 -23
  198. package/src/cost.ts +579 -0
  199. package/src/diagnostics.ts +442 -0
  200. package/src/emit.ts +201 -0
  201. package/src/entry-overrides.ts +263 -48
  202. package/src/format.ts +261 -0
  203. package/src/index.ts +34 -4
  204. package/src/ir.ts +99 -0
  205. package/src/lex.ts +59 -4
  206. package/src/limits.ts +2 -3
  207. package/src/lsp/server.ts +460 -0
  208. package/src/modules.ts +446 -0
  209. package/src/parse.ts +655 -21
  210. package/src/std-bundle.ts +91 -0
  211. package/src/std-library.ts +12 -0
  212. package/src/typecheck.ts +3876 -0
  213. package/src/version.ts +4 -16
  214. package/std/SEMANTICS.md +130 -0
  215. package/std/settlements/advance.hsx +177 -0
  216. package/std/settlements/cancellable_booking.hsx +87 -0
  217. package/std/settlements/captured_payment.hsx +144 -0
  218. package/std/settlements/conditional_disbursement.hsx +185 -0
  219. package/std/settlements/credit_facility.hsx +167 -0
  220. package/std/settlements/held_payment.hsx +578 -0
  221. package/std/settlements/index.hsx +3 -0
  222. package/std/settlements/instant_transfer.hsx +190 -0
  223. package/std/settlements/metered.hsx +67 -0
  224. package/std/settlements/pooled_split.hsx +68 -0
  225. package/std/settlements/premium_forward.hsx +257 -0
  226. package/std/settlements/reconciled_payout.hsx +77 -0
  227. package/std/settlements/recurring_collection.hsx +27 -0
  228. package/std/settlements/rotating_pool.hsx +252 -0
  229. package/std/settlements/scheduled.hsx +641 -0
  230. package/std/settlements/security_deposit.hsx +166 -0
  231. package/std/settlements/settlement_batch.hsx +122 -0
  232. package/std/settlements/swap.hsx +1114 -0
  233. package/std/settlements/threshold_pool.hsx +221 -0
  234. package/std/settlements/weighted_distribution.hsx +141 -0
  235. package/dist/src/archetypes.d.ts +0 -17
  236. package/dist/src/archetypes.d.ts.map +0 -1
  237. package/dist/src/archetypes.js +0 -327
  238. package/dist/src/archetypes.js.map +0 -1
  239. package/dist/src/check.d.ts +0 -12
  240. package/dist/src/check.d.ts.map +0 -1
  241. package/dist/src/check.js +0 -1910
  242. package/dist/src/check.js.map +0 -1
  243. package/dist/src/lower.d.ts +0 -146
  244. package/dist/src/lower.d.ts.map +0 -1
  245. package/dist/src/lower.js +0 -4570
  246. package/dist/src/lower.js.map +0 -1
  247. package/dist/src/model.d.ts +0 -467
  248. package/dist/src/model.d.ts.map +0 -1
  249. package/dist/src/model.js +0 -15
  250. package/dist/src/model.js.map +0 -1
  251. package/spec/hsx-ir.schema.json +0 -920
  252. package/src/archetypes.ts +0 -340
  253. package/src/check.ts +0 -2938
  254. package/src/lower.ts +0 -5416
  255. package/src/model.ts +0 -519
@@ -0,0 +1,47 @@
1
+ <!-- Generated by scripts/docs/build.ts for HSX 1.0.0. Do not edit. -->
2
+
3
+ # threshold_pool
4
+
5
+ Source: [`std/settlements/threshold_pool.hsx`](../../../std/settlements/threshold_pool.hsx)
6
+
7
+ ## Export
8
+
9
+ `threshold_pool<C>`
10
+
11
+ ## Parameters
12
+
13
+ - `contributor: optional<party>`
14
+ - `beneficiary: optional<party>`
15
+ - `target: money<C>`
16
+ - `commitment: money<C>`
17
+ - `max_contributors: optional<integer>`
18
+ - `close_by: date`
19
+ - `close_policy: optional<text>`
20
+ - `overfund_policy: optional<text>`
21
+ - `cancel_policy: optional<text>`
22
+ - `fail_policy: optional<text>`
23
+ - `beneficiary_account: optional<account<C>>`
24
+ - `memo: optional<text>`
25
+ - `contribution_instrument: optional<block>`
26
+ - `wording: optional<block>`
27
+
28
+ ## Decision ports
29
+
30
+ None.
31
+
32
+ ## Actions and clauses
33
+
34
+ | Action | Clauses lowered |
35
+ | ---------- | ------------------------------------------------- |
36
+ | `create` | `agent description`, `moves`, `steps`, `summary` |
37
+ | `activate` | `due`, `moves`, `steps`, `summary` |
38
+ | `fail` | `due`, `moves`, `steps`, `summary` |
39
+ | `close` | `agent description`, `moves`, `steps`, `summary` |
40
+ | `create` | `agent description`, `moves`, `steps`, `summary` |
41
+ | `cancel` | `agent description`, `moves`, `steps`, `summary` |
42
+ | `collect` | `agent description`, `moves`, `steps`, `summary` |
43
+ | `refund` | `agent description`, `moves`, `steps`, `summary` |
44
+ | `create` | `agent description`, `examples`, `moves`, `steps` |
45
+ | `activate` | `due`, `examples`, `moves`, `steps` |
46
+ | `fail` | `due`, `examples`, `moves`, `steps` |
47
+ | `close` | `agent description`, `examples`, `moves`, `steps` |
@@ -0,0 +1,39 @@
1
+ <!-- Generated by scripts/docs/build.ts for HSX 1.0.0. Do not edit. -->
2
+
3
+ # weighted_distribution
4
+
5
+ Source: [`std/settlements/weighted_distribution.hsx`](../../../std/settlements/weighted_distribution.hsx)
6
+
7
+ ## Export
8
+
9
+ `weighted_distribution<C>`
10
+
11
+ ## Parameters
12
+
13
+ - `source: party`
14
+ - `recipient: party`
15
+ - `amount: money<C>`
16
+ - `record_at: date`
17
+ - `weight: optional<money<C>>`
18
+ - `max_recipients: optional<integer>`
19
+ - `snapshot: optional<condition>`
20
+ - `rounding_policy: optional<text>`
21
+ - `withholding_policy: optional<text>`
22
+ - `correction_policy: optional<text>`
23
+ - `flat: optional<block>`
24
+ - `id_prefix_override: optional<text>`
25
+
26
+ ## Decision ports
27
+
28
+ - `snapshot`
29
+
30
+ ## Actions and clauses
31
+
32
+ | Action | Clauses lowered |
33
+ | ---------- | ------------------------------------------------------------------------------------------------- |
34
+ | `create` | `agent description`, `moves`, `steps`, `summary` |
35
+ | `snapshot` | `agent description`, `capture input`, `description`, `input`, `moves`, `port`, `steps`, `summary` |
36
+ | `create` | `agent description`, `moves`, `steps`, `summary` |
37
+ | `payout` | `agent description`, `computes distribute`, `moves`, `steps`, `summary` |
38
+ | `create` | `agent description`, `moves`, `requires refs`, `steps`, `summary` |
39
+ | `payout` | `computes distribute`, `due`, `moves`, `steps`, `summary` |
@@ -0,0 +1,20 @@
1
+ <!-- Generated by scripts/docs/build.ts for HSX 1.0.0. Do not edit. -->
2
+
3
+ # Types
4
+
5
+ HSX checks types before it emits UDL. Money and account values carry a currency parameter, and no implicit currency conversion exists.
6
+
7
+ | Kind | Meaning |
8
+ | ----------- | -------------------------------------------------------------------- |
9
+ | `account` | A currency-indexed ledger account reference. |
10
+ | `boolean` | A compile-time or stored true or false value. |
11
+ | `bps` | An integer basis-point value. |
12
+ | `condition` | A declared decision port. |
13
+ | `date` | A stored date-time string checked by UDL. |
14
+ | `integer` | A bounded whole number. |
15
+ | `money` | A currency-indexed minor-unit amount. |
16
+ | `party` | A declared program party. |
17
+ | `percent` | An exact percentage with basis-point precision. |
18
+ | `ref` | A typed reference to another instrument. |
19
+ | `text` | A finite string value. |
20
+ | `unknown` | An internal checker result that source must resolve before lowering. |
@@ -0,0 +1,13 @@
1
+ <!-- Generated by scripts/docs/build.ts for HSX 1.0.0. Do not edit. -->
2
+
3
+ # UDL output
4
+
5
+ An accepted compile returns three artifacts.
6
+
7
+ | Artifact | Contents |
8
+ | -------------- | ----------------------------------------------------------------------------------- |
9
+ | `document` | The canonical UDL object. Serialize it with `serializeUdl` from `@hyperscale0/udl`. |
10
+ | `originMap` | Canonical UDL paths mapped to authored HSX spans. |
11
+ | `costManifest` | The deterministic manifest pinned to the supplied rate card. |
12
+
13
+ Canonical UDL sorts object keys, preserves array order, uses two-space JSON indentation, and ends with one line feed.
@@ -0,0 +1,21 @@
1
+ program juniper_forge_contract "Juniper Forge contract"
2
+ import { instant_transfer, scheduled } from "std/settlements"
3
+
4
+ party samira_nassar: person
5
+ party juniper_forge_interiors: business
6
+
7
+ settlement concept_fee = instant_transfer {
8
+ payer: samira_nassar
9
+ payee: juniper_forge_interiors
10
+ amount: conceptFee: money(SAR)
11
+ fees { samira_nassar: 1% }
12
+ }
13
+
14
+ settlement build_installments = scheduled {
15
+ payer: samira_nassar
16
+ payee: juniper_forge_interiors
17
+ amount: buildPrice: money(SAR)
18
+ count: 3
19
+ every: P30D
20
+ first_due: firstBuildPaymentAt
21
+ }
@@ -0,0 +1,41 @@
1
+ # Two-instrument reading session, 2026-09-02
2
+
3
+ The reader worked only inside the HSX public export. It read the README, skill,
4
+ guide, grammar, types, and generated standard-library pages.
5
+
6
+ ## First run
7
+
8
+ Attempts: 3. Result: environment refusal before language checking.
9
+
10
+ ```text
11
+ $ hsx check reading-test.hsx
12
+ zsh: command not found: hsx
13
+ exit 127
14
+
15
+ $ npm exec --offline --package=. -- hsx check reading-test.hsx
16
+ sh: hsx: command not found
17
+ exit 127
18
+
19
+ $ bun run src/cli.ts check reading-test.hsx
20
+ SyntaxError: Export named 'udlEffectKinds' not found in the cached UDL beta package
21
+ exit 1
22
+ ```
23
+
24
+ The public docs did not distinguish the installed command from the source
25
+ checkout command. The provisional pre-publication clone also resolved an old
26
+ UDL package. The README and first-program guide now name the exact checkout
27
+ command. The rerun used the local UDL rc.1 package already proved by the
28
+ exporter.
29
+
30
+ ## Rerun after the docs repair
31
+
32
+ Attempts: 1. Result: accepted with no diagnostics.
33
+
34
+ ```text
35
+ $ bun run bin/hsx.ts check reading-test.hsx
36
+ [no diagnostics]
37
+ exit 0
38
+ ```
39
+
40
+ The accepted program is in
41
+ [`2026-09-02-two-instruments.hsx`](./2026-09-02-two-instruments.hsx).
@@ -0,0 +1,45 @@
1
+ # 1 · Your first program
2
+
3
+ [`tip-jar.hsx`](./tip-jar.hsx) is the smallest HSX program that moves money.
4
+ A listener tips a podcast host and the money goes straight through.
5
+
6
+ ```bash
7
+ hsx check tip-jar.hsx # prints nothing, exits 0
8
+ hsx build tip-jar.hsx # the compiled IR
9
+ ```
10
+
11
+ ## The four things in the file
12
+
13
+ **`program tip_jar "Tip jar"`** names the company. Exactly one per file. The
14
+ name is `snake_case` and becomes the IR document's `product`; the title is
15
+ optional and is what people read.
16
+
17
+ **`import { instant_transfer } from "std/settlements"`** brings in one settlement brick.
18
+ `"settlement"` is the only module there is, and it holds seventeen settlement
19
+ bricks. A brick you have not imported cannot be instantiated, so the imports
20
+ at the top tell you what shape of company this is before you read a line of it.
21
+
22
+ **`party listener: person`** declares who is involved. A party is a `person`
23
+ or a `business`. Both are needed here: `instant_transfer` names a payer and a
24
+ payee, and both must be declared parties.
25
+
26
+ **`settlement tip = instant_transfer { … }`** is the money. `payer` and
27
+ `payee` name declared parties; `amount: tipAmount: money(SAR)` declares a
28
+ field the instance stores, called `tipAmount`, holding an amount in SAR.
29
+
30
+ That doubled colon reads oddly the first time. The archetype's parameter is
31
+ `amount`; its value is a typed binding, `tipAmount: money(SAR)`, which names
32
+ the field AND its type. You choose the field name because it shows up in the
33
+ generated API, and `amount` is HSX's word, not your product's.
34
+
35
+ ## What it compiles to
36
+
37
+ One instrument with one action that moves money, and one money event, `tip_pay_1`.
38
+ Notice what is NOT there: no escrow flag, because nothing is held. Compare
39
+ that with example 2, where the money waits for someone to decide.
40
+
41
+ ## Try breaking it
42
+
43
+ Change `payee: host` to `payee: hosts` and run `hsx check` again. The
44
+ compiler tells you there is no party named `hosts`, and points at the column
45
+ where you wrote it. That is example 3's whole subject.
@@ -0,0 +1,17 @@
1
+ // The smallest HSX program that moves money.
2
+ //
3
+ // A listener tips a podcast host. Nothing is held, nobody has to decide
4
+ // anything, and the money goes straight through.
5
+
6
+ program tip_jar "Tip jar"
7
+
8
+ import { instant_transfer } from "std/settlements"
9
+
10
+ party listener: person
11
+ party host: business
12
+
13
+ settlement tip = instant_transfer {
14
+ payer: listener
15
+ payee: host
16
+ amount: tipAmount: money(SAR)
17
+ }
@@ -0,0 +1,96 @@
1
+ # 2 · Imports, settlement bricks, and the port
2
+
3
+ [`photo-booth.hsx`](./photo-booth.hsx) is a photo-booth rental company. The
4
+ renter pays up front, but the money does not reach the company until the booth
5
+ is delivered.
6
+
7
+ ```bash
8
+ hsx check photo-booth.hsx
9
+ hsx build photo-booth.hsx --out ir.json
10
+ ```
11
+
12
+ ## Holding money changes everything
13
+
14
+ `instant_transfer` in example 1 had no custody: money left the payer and
15
+ arrived at the payee in one step. `held_payment` puts the money in the
16
+ settlement's own escrow and holds it there until something says to let go.
17
+ That "something" is the interesting part.
18
+
19
+ ```hsx
20
+ release: port confirm_delivery
21
+ ```
22
+
23
+ A **port** is the typed seam where the tenant's own backend decides. HSX does
24
+ not model delivery, and the platform has no way to know whether a booth showed
25
+ up. So the language does not pretend: it declares who may answer the question
26
+ and what the answer looks like, and the answer comes from outside.
27
+
28
+ ```hsx
29
+ port confirm_delivery {
30
+ allowed: [company]
31
+ shape {
32
+ boothId: id(booth)
33
+ deliveredOn: date
34
+ }
35
+ }
36
+ ```
37
+
38
+ `allowed` is required and must name declared parties. `shape` is optional; its
39
+ field types are `text`, `date`, `id(<asset>)`, and `money(<CUR>)`.
40
+
41
+ ## Assets are the things, not the money
42
+
43
+ ```hsx
44
+ asset booth: good { title_transfer: off_platform }
45
+ ```
46
+
47
+ `title_transfer: off_platform` records that ownership of the booth changes
48
+ hands outside the platform. Money stays on-platform; this flag describes the
49
+ object.
50
+
51
+ ## Fees have a side, and the side matters
52
+
53
+ ```hsx
54
+ fees { renter: 1%, company: 3% }
55
+ ```
56
+
57
+ Those two percentages behave completely differently, and the difference is
58
+ fixed by the compiler, not by you:
59
+
60
+ - The **payer-side** fee (`renter: 1%`) is charged **on top** at funding and
61
+ moves renter → platform directly. It never enters escrow, so no exit
62
+ refunds it.
63
+ - The **payee-side** fee (`company: 3%`) is **carved out** of the amount at
64
+ release. The renter pays the booking fee, and the company receives 97% of it.
65
+
66
+ ## Cancellation splits to the basis point
67
+
68
+ ```hsx
69
+ on_cancel(funded) { renter: 90%, company: 10% }
70
+ ```
71
+
72
+ The shares must total exactly 100%. Not 99.99%, not 100.01%. A 0% share is
73
+ refused outright: drop the party from the block instead of promising them
74
+ nothing.
75
+
76
+ ## Where the piece fields come from
77
+
78
+ Look at the compiled instrument's `fields` and you will find `piece1Amount`,
79
+ `piece2Amount`, `piece3Amount` next to `bookingFee`, plus a `partitions`
80
+ clause proving they sum to it exactly.
81
+
82
+ Release pays out 97/3 (company, platform). Cancellation pays out 90/10
83
+ (renter, company). The compiler cuts the amount at every boundary either exit
84
+ cares about, which lands on 90 / 7 / 3, and then every exit is a whole number
85
+ of pieces:
86
+
87
+ | Piece | Release | Cancel |
88
+ | -------------------- | -------- | ------- |
89
+ | `piece1Amount` (90%) | company | renter |
90
+ | `piece2Amount` (7%) | company | company |
91
+ | `piece3Amount` (3%) | platform | company |
92
+
93
+ Each piece is `floor(bookingFee × bps / 10000)` in minor units, and the
94
+ division remainder goes to `piece1Amount`. Nothing rounds; nothing is lost.
95
+ This is what "the runtime does the plumbing" actually means, and it is why the
96
+ source is 20 lines instead of a lifecycle table.
@@ -0,0 +1,37 @@
1
+ // Imports, archetypes, and the decision port.
2
+ //
3
+ // A photo-booth rental company. The renter pays up front, but the money sits
4
+ // in escrow until the booth is delivered. The company's own backend answers
5
+ // "was it delivered?" through the `confirm_delivery` port; nothing on the
6
+ // platform guesses. If the booking falls through after funding, the renter
7
+ // gets almost everything back and the company keeps a small penalty.
8
+
9
+ program photo_booth "Photo booth rentals"
10
+
11
+ import { held_payment } from "std/settlements"
12
+
13
+ party renter: person
14
+ party company: business
15
+
16
+ asset booth: good { title_transfer: off_platform }
17
+
18
+ settlement booking = held_payment {
19
+ payer: renter
20
+ payee: company
21
+ amount: bookingFee: money(SAR)
22
+
23
+ // The platform takes 3% out of the company's payout, and charges the
24
+ // renter 1% on top of the booking fee at funding.
25
+ fees { renter: 1%, company: 3% }
26
+
27
+ release: port confirm_delivery
28
+ on_cancel(funded) { renter: 90%, company: 10% }
29
+ }
30
+
31
+ port confirm_delivery {
32
+ allowed: [company]
33
+ shape {
34
+ boothId: id(booth)
35
+ deliveredOn: date
36
+ }
37
+ }
@@ -0,0 +1,68 @@
1
+ # 3 · Reading diagnostics
2
+
3
+ [`corner-shop.hsx`](./corner-shop.hsx) is wrong on purpose. Three mistakes,
4
+ one per idea. [`corner-shop-fixed.hsx`](./corner-shop-fixed.hsx) is the same
5
+ program repaired.
6
+
7
+ ```bash
8
+ hsx check corner-shop.hsx # exits 1
9
+ hsx check corner-shop-fixed.hsx # exits 0, prints nothing
10
+ ```
11
+
12
+ ## What comes back
13
+
14
+ ```
15
+ corner-shop.hsx:15:12: error [check] settlement basket payee must name a declared party; there is no party named grocer
16
+ corner-shop.hsx:17:17: error [check] settlement basket decides release through port confirm_pickup, but no port with that name is declared
17
+ corner-shop.hsx:18:21: error [check] the on_cancel split must account for exactly 100%; these shares total 60%
18
+ ```
19
+
20
+ Every diagnostic carries a 1-indexed line and column, a severity, the stage
21
+ that raised it, and one sentence. Coordinates always point at the source you
22
+ wrote, never at a path inside the compiled IR.
23
+
24
+ ## The three mistakes
25
+
26
+ **Line 15, a name that was never declared.** `payee: grocer` names a party
27
+ that does not exist. Parties, assets, settlements, and ports share one flat
28
+ namespace, and everything in it must be declared. The fix is one line:
29
+ `party grocer: business`.
30
+
31
+ **Line 17, a decision with nothing behind it.** `release: port confirm_pickup`
32
+ says the release is decided by a port called `confirm_pickup`, and no such
33
+ port is declared. HSX will not invent a decider: someone real has to answer,
34
+ and the file has to say who. The fix declares the port and names who may
35
+ answer it.
36
+
37
+ **Line 18, money that does not add up.** `on_cancel(funded) { shopper: 60% }`
38
+ leaves 40% of the held amount unaccounted for. There is no default recipient
39
+ and no implicit remainder. The message does the arithmetic for you and says
40
+ what the shares actually total.
41
+
42
+ ## All three at once, and why
43
+
44
+ The compiler reports every error it can rather than stopping at the first, so
45
+ one run tells you everything you have to fix.
46
+
47
+ But it will not report errors from two different stages in the same run.
48
+ Compiling is three stages, parse then check then lower, and a stage only runs
49
+ the one before it produced something to work with. All three errors here are
50
+ `[check]`. Introduce a syntax error, say by deleting a closing brace, and you
51
+ get one `[parse]` diagnostic and nothing else: a file that does not parse has
52
+ no meaning to check, so anything else the compiler said about it would be
53
+ guesswork.
54
+
55
+ ## Errors, warnings, verdicts
56
+
57
+ There are three verdicts:
58
+
59
+ | Verdict | Artifacts | |
60
+ | --------- | --------- | ------------------------------------------ |
61
+ | `valid` | present | Nothing to say. |
62
+ | `warning` | present | It compiled, and the lint voice has notes. |
63
+ | `invalid` | absent | It cannot be compiled. |
64
+
65
+ Warnings are lint, not soft errors: a party nobody involves, an import nothing
66
+ instantiates, a 0% fee, a port nothing releases through. They never block, and
67
+ the artifacts of a `warning` compile are complete and usable. If you want them
68
+ to block in CI, `hsx check --strict` exits 1 on a warning.
@@ -0,0 +1,22 @@
1
+ // The same program with all three mistakes repaired: the grocer is declared,
2
+ // the port the release decides through exists, and the cancellation split
3
+ // accounts for the whole held amount.
4
+
5
+ program corner_shop "Corner shop"
6
+
7
+ import { held_payment } from "std/settlements"
8
+
9
+ party shopper: person
10
+ party grocer: business
11
+
12
+ settlement basket = held_payment {
13
+ payer: shopper
14
+ payee: grocer
15
+ amount: basketTotal: money(SAR)
16
+ release: port confirm_pickup
17
+ on_cancel(funded) { shopper: 60%, grocer: 40% }
18
+ }
19
+
20
+ port confirm_pickup {
21
+ allowed: [grocer]
22
+ }
@@ -0,0 +1,19 @@
1
+ // This program is wrong on purpose. Three separate mistakes, one per idea:
2
+ // a name that was never declared, a decision with no port behind it, and a
3
+ // cancellation split that does not add up.
4
+ //
5
+ // Run `hsx check corner-shop.hsx` and read what comes back.
6
+
7
+ program corner_shop "Corner shop"
8
+
9
+ import { held_payment } from "std/settlements"
10
+
11
+ party shopper: person
12
+
13
+ settlement basket = held_payment {
14
+ payer: shopper
15
+ payee: grocer
16
+ amount: basketTotal: money(SAR)
17
+ release: port confirm_pickup
18
+ on_cancel(funded) { shopper: 60% }
19
+ }
@@ -0,0 +1,84 @@
1
+ # 4 · A complete product
2
+
3
+ [`study-hall.hsx`](./study-hall.hsx) is a tutoring marketplace: three
4
+ settlements, three different jobs, one program.
5
+
6
+ ```bash
7
+ hsx check study-hall.hsx
8
+ hsx build study-hall.hsx --out ir.json
9
+ ```
10
+
11
+ | Settlement | Brick | What it does |
12
+ | -------------- | ------------------ | ------------------------------------------------------------------------------------------------------ |
13
+ | `lesson` | `held_payment` | Holds the student's payment until the tutor confirms the lesson, or until the stored end date arrives. |
14
+ | `kit_deposit` | `security_deposit` | Reserves the equipment deposit on the student's own account. Claimed on damage, returned intact. |
15
+ | `tutor_payout` | `pooled_split` | Splits the weekly pool 55/45 between the two tutors, remainder to the lead. |
16
+
17
+ ## A deposit is not a payment
18
+
19
+ ```hsx
20
+ settlement kit_deposit = security_deposit {
21
+ payer: student
22
+ holder: tutor
23
+ amount: kitDeposit: money(SAR)
24
+ claim: port assess_kit
25
+ return: port release_kit
26
+ }
27
+ ```
28
+
29
+ The money never leaves the student's account. A deposit is a **reservation**:
30
+ placed as a hold, then either posted to the holder (`claim`) or voided back to
31
+ the payer (`return`). That is why one settlement needs two ports and why they
32
+ must be different ones. Two exits, two decisions, and the hold pairing law
33
+ accounts for the full amount on both.
34
+
35
+ ## Nobody answering is an answer
36
+
37
+ ```hsx
38
+ release: port confirm_lesson | at(lessonEndsAt)
39
+ ```
40
+
41
+ `| at(<date field>)` gives the release a stored date to fall back on. If
42
+ nobody answers the port, the date releases the money to the tutor.
43
+
44
+ This is the one place HSX lets a deadline decide money, and it is deliberately
45
+ not symmetric: inaction resolves in the direction the parties already agreed
46
+ on, rather than stranding the payment in escrow forever. Only `held_payment`'s
47
+ `release` accepts it. Every other port must be answered.
48
+
49
+ ## The remainder has an owner
50
+
51
+ ```hsx
52
+ split { lead_tutor: 55%, tutor: 45%, remainder_to: lead_tutor }
53
+ ```
54
+
55
+ Percentages of an integer amount of minor units almost never divide evenly.
56
+ Every piece is floored, and the leftover minor units go somewhere explicit:
57
+ `remainder_to` names which recipient carries them, and without it the first
58
+ share does. Nobody's fraction of a halala goes missing, and nobody's is
59
+ invented.
60
+
61
+ A pool needs at least two recipients, and the funder cannot be one of them. A
62
+ pool that distributes to one person is a transfer, and there is an archetype
63
+ for that.
64
+
65
+ ## What it compiles to
66
+
67
+ Three instruments, `lesson`, `kit_deposit`, and `tutor_payout`, plus nine money events:
68
+
69
+ ```
70
+ lesson_fund
71
+ lesson_release_tutor
72
+ lesson_release_platform
73
+ lesson_abandon
74
+ kit_deposit_hold_1
75
+ tutor_payout_pool_1
76
+ tutor_payout_pool_2
77
+ tutor_payout_payout_1
78
+ tutor_payout_payout_2
79
+ ```
80
+
81
+ One program carries at most **20** money events (`MONEY_EVENT_BUDGET`). This
82
+ one uses nine. Every installment anchor, fee leg, cancellation leg, and
83
+ abandonment refund counts one, so the budget is the real ceiling on how much
84
+ company fits in a single file. Past it, split the product.
@@ -0,0 +1,67 @@
1
+ // A complete small product: a tutoring marketplace.
2
+ //
3
+ // Three settlements, three different jobs:
4
+ //
5
+ // lesson a student's payment is held until the tutor confirms the
6
+ // lesson happened; the platform's 8% comes out of the payout
7
+ // kit_deposit the tutor's equipment deposit is a reservation on the
8
+ // student's own account, claimed on damage or returned intact
9
+ // tutor_payout the marketplace's weekly pool, split between the two tutors
10
+ // who ran the sessions, with the remainder going to the lead
11
+ //
12
+ // Everything a caller can decide goes through a port, and every port names
13
+ // exactly who may answer it.
14
+
15
+ program study_hall "Study hall"
16
+
17
+ import { held_payment, security_deposit, pooled_split } from "std/settlements"
18
+
19
+ party student: person
20
+ party tutor: business
21
+ party lead_tutor: business
22
+ party school: business
23
+
24
+ asset lesson_kit: good { title_transfer: on_platform }
25
+
26
+ settlement lesson = held_payment {
27
+ payer: student
28
+ payee: tutor
29
+ amount: lessonFee: money(SAR)
30
+ fees { tutor: 8% }
31
+ release: port confirm_lesson | at(lessonEndsAt)
32
+ }
33
+
34
+ settlement kit_deposit = security_deposit {
35
+ payer: student
36
+ holder: tutor
37
+ amount: kitDeposit: money(SAR)
38
+ claim: port assess_kit
39
+ return: port release_kit
40
+ }
41
+
42
+ settlement tutor_payout = pooled_split {
43
+ payer: school
44
+ amount: weeklyPool: money(SAR)
45
+ payout_due: payoutDate
46
+ split { lead_tutor: 55%, tutor: 45%, remainder_to: lead_tutor }
47
+ }
48
+
49
+ // The tutor confirms the lesson happened. If nobody answers, `at(lessonEndsAt)`
50
+ // releases the money to the tutor on the stored date: inaction resolves in the
51
+ // direction the parties already agreed on, instead of stranding the payment.
52
+ port confirm_lesson {
53
+ allowed: [tutor]
54
+ shape {
55
+ kitId: id(lesson_kit)
56
+ lessonNotes: text
57
+ }
58
+ }
59
+
60
+ port assess_kit {
61
+ allowed: [tutor]
62
+ shape { damageReport: text }
63
+ }
64
+
65
+ port release_kit {
66
+ allowed: [tutor]
67
+ }
@@ -0,0 +1,9 @@
1
+ # Watch club
2
+
3
+ This reference program joins the algebra needed for one club product. It keeps
4
+ each watch attached to explicit sale, deposit, and trade rules. Membership due
5
+ dates open periods, while callers still author every money action. Seller
6
+ payouts close through stored provider evidence.
7
+
8
+ The acceptance proof lives in
9
+ `toolchain/system-tests/test/watch-club.acceptance.spec.ts`.