@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.
- package/CHANGELOG.md +58 -9
- package/LICENSING.md +3 -3
- package/README.md +39 -149
- package/TRADEMARKS.md +4 -4
- package/bin/hsx.ts +2 -0
- package/dist/bin/hsx.js +2 -0
- package/dist/bin/hsx.js.map +1 -1
- package/dist/src/ast.d.ts +135 -10
- package/dist/src/ast.d.ts.map +1 -1
- package/dist/src/ast.js +24 -0
- package/dist/src/ast.js.map +1 -1
- package/dist/src/cli.d.ts +4 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +172 -16
- package/dist/src/cli.js.map +1 -1
- package/dist/src/compile.d.ts +42 -7
- package/dist/src/compile.d.ts.map +1 -1
- package/dist/src/compile.js +82 -16
- package/dist/src/compile.js.map +1 -1
- package/dist/src/cost.d.ts +78 -0
- package/dist/src/cost.d.ts.map +1 -0
- package/dist/src/cost.js +369 -0
- package/dist/src/cost.js.map +1 -0
- package/dist/src/diagnostics.d.ts +11 -0
- package/dist/src/diagnostics.d.ts.map +1 -0
- package/dist/src/diagnostics.js +420 -0
- package/dist/src/diagnostics.js.map +1 -0
- package/dist/src/emit.d.ts +34 -0
- package/dist/src/emit.d.ts.map +1 -0
- package/dist/src/emit.js +137 -0
- package/dist/src/emit.js.map +1 -0
- package/dist/src/entry-overrides.d.ts +17 -3
- package/dist/src/entry-overrides.d.ts.map +1 -1
- package/dist/src/entry-overrides.js +180 -35
- package/dist/src/entry-overrides.js.map +1 -1
- package/dist/src/format.d.ts +10 -0
- package/dist/src/format.d.ts.map +1 -0
- package/dist/src/format.js +183 -0
- package/dist/src/format.js.map +1 -0
- package/dist/src/index.d.ts +11 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +10 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/ir.d.ts +66 -0
- package/dist/src/ir.d.ts.map +1 -0
- package/dist/src/ir.js +15 -0
- package/dist/src/ir.js.map +1 -0
- package/dist/src/lex.d.ts +9 -1
- package/dist/src/lex.d.ts.map +1 -1
- package/dist/src/lex.js +50 -4
- package/dist/src/lex.js.map +1 -1
- package/dist/src/limits.d.ts +2 -3
- package/dist/src/limits.d.ts.map +1 -1
- package/dist/src/limits.js +2 -3
- package/dist/src/limits.js.map +1 -1
- package/dist/src/lsp/server.d.ts +32 -0
- package/dist/src/lsp/server.d.ts.map +1 -0
- package/dist/src/lsp/server.js +391 -0
- package/dist/src/lsp/server.js.map +1 -0
- package/dist/src/modules.d.ts +38 -0
- package/dist/src/modules.d.ts.map +1 -0
- package/dist/src/modules.js +368 -0
- package/dist/src/modules.js.map +1 -0
- package/dist/src/parse.d.ts.map +1 -1
- package/dist/src/parse.js +590 -17
- package/dist/src/parse.js.map +1 -1
- package/dist/src/std-bundle.d.ts +2 -0
- package/dist/src/std-bundle.d.ts.map +1 -0
- package/dist/src/std-bundle.js +88 -0
- package/dist/src/std-bundle.js.map +1 -0
- package/dist/src/std-library.d.ts +5 -0
- package/dist/src/std-library.d.ts.map +1 -0
- package/dist/src/std-library.js +9 -0
- package/dist/src/std-library.js.map +1 -0
- package/dist/src/typecheck.d.ts +8 -0
- package/dist/src/typecheck.d.ts.map +1 -0
- package/dist/src/typecheck.js +2958 -0
- package/dist/src/typecheck.js.map +1 -0
- package/dist/src/version.d.ts +4 -15
- package/dist/src/version.d.ts.map +1 -1
- package/dist/src/version.js +4 -15
- package/dist/src/version.js.map +1 -1
- package/docs/README.md +26 -0
- package/docs/guide/01-first-program.md +24 -0
- package/docs/guide/02-money.md +25 -0
- package/docs/guide/03-instruments.md +22 -0
- package/docs/guide/04-lifecycles.md +25 -0
- package/docs/guide/05-fees-and-splits.md +45 -0
- package/docs/guide/06-schedules.md +22 -0
- package/docs/guide/07-composition.md +46 -0
- package/docs/guide/08-writing-a-module.md +26 -0
- package/docs/guide/09-cost.md +24 -0
- package/docs/guide/10-diagnostics.md +27 -0
- package/docs/llms-full.txt +1739 -0
- package/docs/llms.txt +45 -0
- package/docs/playground.md +54 -0
- package/docs/reference/cli.md +35 -0
- package/docs/reference/diagnostics.md +619 -0
- package/docs/reference/grammar.md +39 -0
- package/docs/reference/std/advance.md +35 -0
- package/docs/reference/std/cancellable_booking.md +35 -0
- package/docs/reference/std/captured_payment.md +43 -0
- package/docs/reference/std/conditional_disbursement.md +33 -0
- package/docs/reference/std/credit_facility.md +35 -0
- package/docs/reference/std/held_payment.md +64 -0
- package/docs/reference/std/instant_transfer.md +30 -0
- package/docs/reference/std/metered.md +27 -0
- package/docs/reference/std/pooled_split.md +26 -0
- package/docs/reference/std/premium_forward.md +39 -0
- package/docs/reference/std/reconciled_payout.md +33 -0
- package/docs/reference/std/recurring_collection.md +25 -0
- package/docs/reference/std/rotating_pool.md +42 -0
- package/docs/reference/std/scheduled.md +51 -0
- package/docs/reference/std/security_deposit.md +46 -0
- package/docs/reference/std/settlement_batch.md +42 -0
- package/docs/reference/std/swap.md +46 -0
- package/docs/reference/std/threshold_pool.md +47 -0
- package/docs/reference/std/weighted_distribution.md +39 -0
- package/docs/reference/types.md +20 -0
- package/docs/reference/udl-output.md +13 -0
- package/docs/sessions/2026-09-02-two-instruments.hsx +21 -0
- package/docs/sessions/2026-09-02-two-instruments.md +41 -0
- package/examples/01-first-program/README.md +45 -0
- package/examples/01-first-program/tip-jar.hsx +17 -0
- package/examples/02-imports-and-archetypes/README.md +96 -0
- package/examples/02-imports-and-archetypes/photo-booth.hsx +37 -0
- package/examples/03-diagnostics/README.md +68 -0
- package/examples/03-diagnostics/corner-shop-fixed.hsx +22 -0
- package/examples/03-diagnostics/corner-shop.hsx +19 -0
- package/examples/04-complete-product/README.md +84 -0
- package/examples/04-complete-product/study-hall.hsx +67 -0
- package/examples/05-watch-club/README.md +9 -0
- package/examples/05-watch-club/watch-club.hsx +124 -0
- package/examples/README.md +18 -0
- package/examples/advance/README.md +3 -0
- package/examples/advance/advance.hsx +13 -0
- package/examples/advance/advance.udl +339 -0
- package/examples/cancellable_booking/README.md +3 -0
- package/examples/cancellable_booking/cancellable_booking.hsx +14 -0
- package/examples/cancellable_booking/cancellable_booking.udl +391 -0
- package/examples/captured_payment/README.md +3 -0
- package/examples/captured_payment/captured_payment.hsx +21 -0
- package/examples/captured_payment/captured_payment.udl +677 -0
- package/examples/conditional_disbursement/README.md +3 -0
- package/examples/conditional_disbursement/conditional_disbursement.hsx +17 -0
- package/examples/conditional_disbursement/conditional_disbursement.udl +412 -0
- package/examples/cost-table.json +386 -0
- package/examples/credit_facility/README.md +3 -0
- package/examples/credit_facility/credit_facility.hsx +27 -0
- package/examples/credit_facility/credit_facility.udl +1373 -0
- package/examples/held_payment/README.md +3 -0
- package/examples/held_payment/held_payment.hsx +13 -0
- package/examples/held_payment/held_payment.udl +507 -0
- package/examples/instant_transfer/README.md +3 -0
- package/examples/instant_transfer/instant_transfer.hsx +16 -0
- package/examples/instant_transfer/instant_transfer.udl +364 -0
- package/examples/metered/README.md +3 -0
- package/examples/metered/metered.hsx +13 -0
- package/examples/metered/metered.udl +247 -0
- package/examples/pooled_split/README.md +3 -0
- package/examples/pooled_split/pooled_split.hsx +15 -0
- package/examples/pooled_split/pooled_split.udl +425 -0
- package/examples/premium_forward/README.md +3 -0
- package/examples/premium_forward/premium_forward.hsx +19 -0
- package/examples/premium_forward/premium_forward.udl +594 -0
- package/examples/reconciled_payout/README.md +3 -0
- package/examples/reconciled_payout/reconciled_payout.hsx +13 -0
- package/examples/reconciled_payout/reconciled_payout.udl +303 -0
- package/examples/recurring_collection/README.md +3 -0
- package/examples/recurring_collection/recurring_collection.hsx +21 -0
- package/examples/recurring_collection/recurring_collection.udl +1130 -0
- package/examples/rotating_pool/README.md +3 -0
- package/examples/rotating_pool/rotating_pool.hsx +18 -0
- package/examples/rotating_pool/rotating_pool.udl +4352 -0
- package/examples/scheduled/README.md +3 -0
- package/examples/scheduled/scheduled.hsx +12 -0
- package/examples/scheduled/scheduled.udl +335 -0
- package/examples/security_deposit/README.md +3 -0
- package/examples/security_deposit/security_deposit.hsx +21 -0
- package/examples/security_deposit/security_deposit.udl +293 -0
- package/examples/settlement_batch/README.md +3 -0
- package/examples/settlement_batch/settlement_batch.hsx +19 -0
- package/examples/settlement_batch/settlement_batch.udl +753 -0
- package/examples/swap/README.md +3 -0
- package/examples/swap/swap.hsx +19 -0
- package/examples/swap/swap.udl +876 -0
- package/examples/threshold_pool/README.md +3 -0
- package/examples/threshold_pool/threshold_pool.hsx +16 -0
- package/examples/threshold_pool/threshold_pool.udl +667 -0
- package/examples/weighted_distribution/README.md +3 -0
- package/examples/weighted_distribution/weighted_distribution.hsx +20 -0
- package/examples/weighted_distribution/weighted_distribution.udl +337 -0
- package/package.json +27 -6
- package/skills/hsx/SKILL.md +483 -0
- package/src/ast.ts +193 -9
- package/src/cli.ts +191 -20
- package/src/compile.ts +160 -23
- package/src/cost.ts +579 -0
- package/src/diagnostics.ts +442 -0
- package/src/emit.ts +201 -0
- package/src/entry-overrides.ts +263 -48
- package/src/format.ts +261 -0
- package/src/index.ts +34 -4
- package/src/ir.ts +99 -0
- package/src/lex.ts +59 -4
- package/src/limits.ts +2 -3
- package/src/lsp/server.ts +460 -0
- package/src/modules.ts +446 -0
- package/src/parse.ts +655 -21
- package/src/std-bundle.ts +91 -0
- package/src/std-library.ts +12 -0
- package/src/typecheck.ts +3876 -0
- package/src/version.ts +4 -16
- package/std/SEMANTICS.md +130 -0
- package/std/settlements/advance.hsx +177 -0
- package/std/settlements/cancellable_booking.hsx +87 -0
- package/std/settlements/captured_payment.hsx +144 -0
- package/std/settlements/conditional_disbursement.hsx +185 -0
- package/std/settlements/credit_facility.hsx +167 -0
- package/std/settlements/held_payment.hsx +578 -0
- package/std/settlements/index.hsx +3 -0
- package/std/settlements/instant_transfer.hsx +190 -0
- package/std/settlements/metered.hsx +67 -0
- package/std/settlements/pooled_split.hsx +68 -0
- package/std/settlements/premium_forward.hsx +257 -0
- package/std/settlements/reconciled_payout.hsx +77 -0
- package/std/settlements/recurring_collection.hsx +27 -0
- package/std/settlements/rotating_pool.hsx +252 -0
- package/std/settlements/scheduled.hsx +641 -0
- package/std/settlements/security_deposit.hsx +166 -0
- package/std/settlements/settlement_batch.hsx +122 -0
- package/std/settlements/swap.hsx +1114 -0
- package/std/settlements/threshold_pool.hsx +221 -0
- package/std/settlements/weighted_distribution.hsx +141 -0
- package/dist/src/archetypes.d.ts +0 -17
- package/dist/src/archetypes.d.ts.map +0 -1
- package/dist/src/archetypes.js +0 -327
- package/dist/src/archetypes.js.map +0 -1
- package/dist/src/check.d.ts +0 -12
- package/dist/src/check.d.ts.map +0 -1
- package/dist/src/check.js +0 -1910
- package/dist/src/check.js.map +0 -1
- package/dist/src/lower.d.ts +0 -146
- package/dist/src/lower.d.ts.map +0 -1
- package/dist/src/lower.js +0 -4570
- package/dist/src/lower.js.map +0 -1
- package/dist/src/model.d.ts +0 -467
- package/dist/src/model.d.ts.map +0 -1
- package/dist/src/model.js +0 -15
- package/dist/src/model.js.map +0 -1
- package/spec/hsx-ir.schema.json +0 -920
- package/src/archetypes.ts +0 -340
- package/src/check.ts +0 -2938
- package/src/lower.ts +0 -5416
- 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`.
|