@hyperscale0/hsx 1.0.0 → 1.0.124

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 (316) hide show
  1. package/CHANGELOG.md +5 -256
  2. package/LICENSE.md +356 -0
  3. package/NOTICE.md +8 -0
  4. package/README.md +44 -44
  5. package/bin/hsx.ts +0 -7
  6. package/dist/bin/hsx.d.ts +0 -4
  7. package/dist/bin/hsx.d.ts.map +1 -1
  8. package/dist/bin/hsx.js +0 -6
  9. package/dist/bin/hsx.js.map +1 -1
  10. package/dist/src/ast.d.ts +113 -308
  11. package/dist/src/ast.d.ts.map +1 -1
  12. package/dist/src/ast.js +7 -71
  13. package/dist/src/ast.js.map +1 -1
  14. package/dist/src/attachment-economics.d.ts +5 -0
  15. package/dist/src/attachment-economics.d.ts.map +1 -0
  16. package/dist/src/attachment-economics.js +55 -0
  17. package/dist/src/attachment-economics.js.map +1 -0
  18. package/dist/src/binding-contract.d.ts +22 -0
  19. package/dist/src/binding-contract.d.ts.map +1 -0
  20. package/dist/src/binding-contract.js +74 -0
  21. package/dist/src/binding-contract.js.map +1 -0
  22. package/dist/src/cli.d.ts +4 -19
  23. package/dist/src/cli.d.ts.map +1 -1
  24. package/dist/src/cli.js +89 -276
  25. package/dist/src/cli.js.map +1 -1
  26. package/dist/src/compile.d.ts +34 -66
  27. package/dist/src/compile.d.ts.map +1 -1
  28. package/dist/src/compile.js +2949 -117
  29. package/dist/src/compile.js.map +1 -1
  30. package/dist/src/cost.d.ts +8 -75
  31. package/dist/src/cost.d.ts.map +1 -1
  32. package/dist/src/cost.js +54 -364
  33. package/dist/src/cost.js.map +1 -1
  34. package/dist/src/diagnostics.d.ts +14 -9
  35. package/dist/src/diagnostics.d.ts.map +1 -1
  36. package/dist/src/diagnostics.js +39 -416
  37. package/dist/src/diagnostics.js.map +1 -1
  38. package/dist/src/examples-bundle.d.ts +3 -0
  39. package/dist/src/examples-bundle.d.ts.map +1 -0
  40. package/dist/src/examples-bundle.js +18 -0
  41. package/dist/src/examples-bundle.js.map +1 -0
  42. package/dist/src/examples.d.ts +10 -0
  43. package/dist/src/examples.d.ts.map +1 -0
  44. package/dist/src/examples.js +2 -0
  45. package/dist/src/examples.js.map +1 -0
  46. package/dist/src/format.d.ts +8 -7
  47. package/dist/src/format.d.ts.map +1 -1
  48. package/dist/src/format.js +9 -175
  49. package/dist/src/format.js.map +1 -1
  50. package/dist/src/header-source.d.ts +3 -0
  51. package/dist/src/header-source.d.ts.map +1 -0
  52. package/dist/src/header-source.js +41 -0
  53. package/dist/src/header-source.js.map +1 -0
  54. package/dist/src/headers.d.ts +64 -0
  55. package/dist/src/headers.d.ts.map +1 -0
  56. package/dist/src/headers.js +167 -0
  57. package/dist/src/headers.js.map +1 -0
  58. package/dist/src/index.d.ts +7 -11
  59. package/dist/src/index.d.ts.map +1 -1
  60. package/dist/src/index.js +4 -9
  61. package/dist/src/index.js.map +1 -1
  62. package/dist/src/keywords.d.ts +56 -0
  63. package/dist/src/keywords.d.ts.map +1 -0
  64. package/dist/src/keywords.js +133 -0
  65. package/dist/src/keywords.js.map +1 -0
  66. package/dist/src/language.d.ts +26 -0
  67. package/dist/src/language.d.ts.map +1 -0
  68. package/dist/src/language.js +440 -0
  69. package/dist/src/language.js.map +1 -0
  70. package/dist/src/lex.d.ts +13 -28
  71. package/dist/src/lex.d.ts.map +1 -1
  72. package/dist/src/lex.js +119 -145
  73. package/dist/src/lex.js.map +1 -1
  74. package/dist/src/parse.d.ts +4 -17
  75. package/dist/src/parse.d.ts.map +1 -1
  76. package/dist/src/parse.js +484 -1036
  77. package/dist/src/parse.js.map +1 -1
  78. package/dist/src/std-bundle.d.ts.map +1 -1
  79. package/dist/src/std-bundle.js +11 -84
  80. package/dist/src/std-bundle.js.map +1 -1
  81. package/dist/src/std-library.d.ts +1 -1
  82. package/dist/src/std-library.d.ts.map +1 -1
  83. package/dist/src/std-library.js +1 -5
  84. package/dist/src/std-library.js.map +1 -1
  85. package/dist/src/tunables.d.ts +7 -0
  86. package/dist/src/tunables.d.ts.map +1 -0
  87. package/dist/src/tunables.js +27 -0
  88. package/dist/src/tunables.js.map +1 -0
  89. package/docs/README.md +528 -26
  90. package/docs/assets/brand/hsx-horizontal-white.svg +1 -0
  91. package/docs/assets/brand/hsx-horizontal.svg +1 -0
  92. package/docs/assets/brand/hsx-stacked-white.svg +1 -0
  93. package/docs/assets/brand/hsx-stacked.svg +1 -0
  94. package/docs/assets/brand/manifest.json +30 -0
  95. package/docs/assets/hsx.svg +11 -0
  96. package/examples/cards.hsx +34 -0
  97. package/examples/collections.hsx +37 -0
  98. package/examples/cost-table.json +633 -385
  99. package/examples/escrow.hsx +24 -0
  100. package/examples/financing.hsx +32 -0
  101. package/examples/insurance.hsx +30 -0
  102. package/examples/lending.hsx +57 -0
  103. package/examples/library.hsx +15 -0
  104. package/examples/marketplace.hsx +32 -0
  105. package/examples/money.hsx +19 -0
  106. package/examples/rental-deposit.hsx +60 -0
  107. package/examples/repair-approval.hsx +44 -0
  108. package/examples/reporting.hsx +28 -0
  109. package/examples/savings.hsx +21 -0
  110. package/examples/serviced.hsx +25 -0
  111. package/examples/wallet.hsx +24 -0
  112. package/package.json +25 -23
  113. package/src/ast.ts +83 -440
  114. package/src/attachment-economics.ts +103 -0
  115. package/src/binding-contract.ts +104 -0
  116. package/src/cli.ts +95 -325
  117. package/src/compile.ts +4063 -209
  118. package/src/cost.ts +71 -574
  119. package/src/diagnostics.ts +57 -437
  120. package/src/examples-bundle.ts +19 -0
  121. package/src/examples.ts +9 -0
  122. package/src/format.ts +12 -256
  123. package/src/header-source.ts +54 -0
  124. package/src/headers.ts +195 -0
  125. package/src/index.ts +18 -33
  126. package/src/keywords.ts +134 -0
  127. package/src/language.ts +629 -0
  128. package/src/lex.ts +131 -192
  129. package/src/parse.ts +489 -1195
  130. package/src/std-bundle.ts +12 -88
  131. package/src/std-library.ts +2 -7
  132. package/src/tunables.ts +36 -0
  133. package/std/cards.hsx +85 -0
  134. package/std/collections.hsx +90 -0
  135. package/std/escrow.hsx +73 -0
  136. package/std/financing.hsx +683 -0
  137. package/std/insurance.hsx +89 -0
  138. package/std/lending.hsx +138 -0
  139. package/std/marketplace.hsx +123 -0
  140. package/std/money.hsx +173 -0
  141. package/std/reporting.hsx +753 -0
  142. package/std/savings.hsx +68 -0
  143. package/std/wallet.hsx +57 -0
  144. package/LICENSE +0 -661
  145. package/LICENSING.md +0 -52
  146. package/TRADEMARKS.md +0 -35
  147. package/dist/src/emit.d.ts +0 -34
  148. package/dist/src/emit.d.ts.map +0 -1
  149. package/dist/src/emit.js +0 -137
  150. package/dist/src/emit.js.map +0 -1
  151. package/dist/src/entry-overrides.d.ts +0 -58
  152. package/dist/src/entry-overrides.d.ts.map +0 -1
  153. package/dist/src/entry-overrides.js +0 -284
  154. package/dist/src/entry-overrides.js.map +0 -1
  155. package/dist/src/ir.d.ts +0 -66
  156. package/dist/src/ir.d.ts.map +0 -1
  157. package/dist/src/ir.js +0 -15
  158. package/dist/src/ir.js.map +0 -1
  159. package/dist/src/limits.d.ts +0 -21
  160. package/dist/src/limits.d.ts.map +0 -1
  161. package/dist/src/limits.js +0 -21
  162. package/dist/src/limits.js.map +0 -1
  163. package/dist/src/lsp/server.d.ts +0 -32
  164. package/dist/src/lsp/server.d.ts.map +0 -1
  165. package/dist/src/lsp/server.js +0 -391
  166. package/dist/src/lsp/server.js.map +0 -1
  167. package/dist/src/modules.d.ts +0 -38
  168. package/dist/src/modules.d.ts.map +0 -1
  169. package/dist/src/modules.js +0 -368
  170. package/dist/src/modules.js.map +0 -1
  171. package/dist/src/typecheck.d.ts +0 -8
  172. package/dist/src/typecheck.d.ts.map +0 -1
  173. package/dist/src/typecheck.js +0 -2958
  174. package/dist/src/typecheck.js.map +0 -1
  175. package/dist/src/version.d.ts +0 -5
  176. package/dist/src/version.d.ts.map +0 -1
  177. package/dist/src/version.js +0 -5
  178. package/dist/src/version.js.map +0 -1
  179. package/docs/guide/01-first-program.md +0 -24
  180. package/docs/guide/02-money.md +0 -25
  181. package/docs/guide/03-instruments.md +0 -22
  182. package/docs/guide/04-lifecycles.md +0 -25
  183. package/docs/guide/05-fees-and-splits.md +0 -45
  184. package/docs/guide/06-schedules.md +0 -22
  185. package/docs/guide/07-composition.md +0 -46
  186. package/docs/guide/08-writing-a-module.md +0 -26
  187. package/docs/guide/09-cost.md +0 -24
  188. package/docs/guide/10-diagnostics.md +0 -27
  189. package/docs/llms-full.txt +0 -1739
  190. package/docs/llms.txt +0 -45
  191. package/docs/playground.md +0 -54
  192. package/docs/reference/cli.md +0 -35
  193. package/docs/reference/diagnostics.md +0 -619
  194. package/docs/reference/grammar.md +0 -39
  195. package/docs/reference/std/advance.md +0 -35
  196. package/docs/reference/std/cancellable_booking.md +0 -35
  197. package/docs/reference/std/captured_payment.md +0 -43
  198. package/docs/reference/std/conditional_disbursement.md +0 -33
  199. package/docs/reference/std/credit_facility.md +0 -35
  200. package/docs/reference/std/held_payment.md +0 -64
  201. package/docs/reference/std/instant_transfer.md +0 -30
  202. package/docs/reference/std/metered.md +0 -27
  203. package/docs/reference/std/pooled_split.md +0 -26
  204. package/docs/reference/std/premium_forward.md +0 -39
  205. package/docs/reference/std/reconciled_payout.md +0 -33
  206. package/docs/reference/std/recurring_collection.md +0 -25
  207. package/docs/reference/std/rotating_pool.md +0 -42
  208. package/docs/reference/std/scheduled.md +0 -51
  209. package/docs/reference/std/security_deposit.md +0 -46
  210. package/docs/reference/std/settlement_batch.md +0 -42
  211. package/docs/reference/std/swap.md +0 -46
  212. package/docs/reference/std/threshold_pool.md +0 -47
  213. package/docs/reference/std/weighted_distribution.md +0 -39
  214. package/docs/reference/types.md +0 -20
  215. package/docs/reference/udl-output.md +0 -13
  216. package/docs/sessions/2026-09-02-two-instruments.hsx +0 -21
  217. package/docs/sessions/2026-09-02-two-instruments.md +0 -41
  218. package/examples/01-first-program/README.md +0 -45
  219. package/examples/01-first-program/tip-jar.hsx +0 -17
  220. package/examples/02-imports-and-archetypes/README.md +0 -96
  221. package/examples/02-imports-and-archetypes/photo-booth.hsx +0 -37
  222. package/examples/03-diagnostics/README.md +0 -68
  223. package/examples/03-diagnostics/corner-shop-fixed.hsx +0 -22
  224. package/examples/03-diagnostics/corner-shop.hsx +0 -19
  225. package/examples/04-complete-product/README.md +0 -84
  226. package/examples/04-complete-product/study-hall.hsx +0 -67
  227. package/examples/05-watch-club/README.md +0 -9
  228. package/examples/05-watch-club/watch-club.hsx +0 -124
  229. package/examples/README.md +0 -18
  230. package/examples/advance/README.md +0 -3
  231. package/examples/advance/advance.hsx +0 -13
  232. package/examples/advance/advance.udl +0 -339
  233. package/examples/cancellable_booking/README.md +0 -3
  234. package/examples/cancellable_booking/cancellable_booking.hsx +0 -14
  235. package/examples/cancellable_booking/cancellable_booking.udl +0 -391
  236. package/examples/captured_payment/README.md +0 -3
  237. package/examples/captured_payment/captured_payment.hsx +0 -21
  238. package/examples/captured_payment/captured_payment.udl +0 -677
  239. package/examples/conditional_disbursement/README.md +0 -3
  240. package/examples/conditional_disbursement/conditional_disbursement.hsx +0 -17
  241. package/examples/conditional_disbursement/conditional_disbursement.udl +0 -412
  242. package/examples/credit_facility/README.md +0 -3
  243. package/examples/credit_facility/credit_facility.hsx +0 -27
  244. package/examples/credit_facility/credit_facility.udl +0 -1373
  245. package/examples/held_payment/README.md +0 -3
  246. package/examples/held_payment/held_payment.hsx +0 -13
  247. package/examples/held_payment/held_payment.udl +0 -507
  248. package/examples/instant_transfer/README.md +0 -3
  249. package/examples/instant_transfer/instant_transfer.hsx +0 -16
  250. package/examples/instant_transfer/instant_transfer.udl +0 -364
  251. package/examples/metered/README.md +0 -3
  252. package/examples/metered/metered.hsx +0 -13
  253. package/examples/metered/metered.udl +0 -247
  254. package/examples/pooled_split/README.md +0 -3
  255. package/examples/pooled_split/pooled_split.hsx +0 -15
  256. package/examples/pooled_split/pooled_split.udl +0 -425
  257. package/examples/premium_forward/README.md +0 -3
  258. package/examples/premium_forward/premium_forward.hsx +0 -19
  259. package/examples/premium_forward/premium_forward.udl +0 -594
  260. package/examples/reconciled_payout/README.md +0 -3
  261. package/examples/reconciled_payout/reconciled_payout.hsx +0 -13
  262. package/examples/reconciled_payout/reconciled_payout.udl +0 -303
  263. package/examples/recurring_collection/README.md +0 -3
  264. package/examples/recurring_collection/recurring_collection.hsx +0 -21
  265. package/examples/recurring_collection/recurring_collection.udl +0 -1130
  266. package/examples/rotating_pool/README.md +0 -3
  267. package/examples/rotating_pool/rotating_pool.hsx +0 -18
  268. package/examples/rotating_pool/rotating_pool.udl +0 -4352
  269. package/examples/scheduled/README.md +0 -3
  270. package/examples/scheduled/scheduled.hsx +0 -12
  271. package/examples/scheduled/scheduled.udl +0 -335
  272. package/examples/security_deposit/README.md +0 -3
  273. package/examples/security_deposit/security_deposit.hsx +0 -21
  274. package/examples/security_deposit/security_deposit.udl +0 -293
  275. package/examples/settlement_batch/README.md +0 -3
  276. package/examples/settlement_batch/settlement_batch.hsx +0 -19
  277. package/examples/settlement_batch/settlement_batch.udl +0 -753
  278. package/examples/swap/README.md +0 -3
  279. package/examples/swap/swap.hsx +0 -19
  280. package/examples/swap/swap.udl +0 -876
  281. package/examples/threshold_pool/README.md +0 -3
  282. package/examples/threshold_pool/threshold_pool.hsx +0 -16
  283. package/examples/threshold_pool/threshold_pool.udl +0 -667
  284. package/examples/weighted_distribution/README.md +0 -3
  285. package/examples/weighted_distribution/weighted_distribution.hsx +0 -20
  286. package/examples/weighted_distribution/weighted_distribution.udl +0 -337
  287. package/skills/hsx/SKILL.md +0 -483
  288. package/src/emit.ts +0 -201
  289. package/src/entry-overrides.ts +0 -428
  290. package/src/ir.ts +0 -99
  291. package/src/limits.ts +0 -20
  292. package/src/lsp/server.ts +0 -460
  293. package/src/modules.ts +0 -446
  294. package/src/typecheck.ts +0 -3876
  295. package/src/version.ts +0 -5
  296. package/std/SEMANTICS.md +0 -130
  297. package/std/settlements/advance.hsx +0 -177
  298. package/std/settlements/cancellable_booking.hsx +0 -87
  299. package/std/settlements/captured_payment.hsx +0 -144
  300. package/std/settlements/conditional_disbursement.hsx +0 -185
  301. package/std/settlements/credit_facility.hsx +0 -167
  302. package/std/settlements/held_payment.hsx +0 -578
  303. package/std/settlements/index.hsx +0 -3
  304. package/std/settlements/instant_transfer.hsx +0 -190
  305. package/std/settlements/metered.hsx +0 -67
  306. package/std/settlements/pooled_split.hsx +0 -68
  307. package/std/settlements/premium_forward.hsx +0 -257
  308. package/std/settlements/reconciled_payout.hsx +0 -77
  309. package/std/settlements/recurring_collection.hsx +0 -27
  310. package/std/settlements/rotating_pool.hsx +0 -252
  311. package/std/settlements/scheduled.hsx +0 -641
  312. package/std/settlements/security_deposit.hsx +0 -166
  313. package/std/settlements/settlement_batch.hsx +0 -122
  314. package/std/settlements/swap.hsx +0 -1114
  315. package/std/settlements/threshold_pool.hsx +0 -221
  316. package/std/settlements/weighted_distribution.hsx +0 -141
@@ -0,0 +1,753 @@
1
+ header reporting
2
+
3
+ // Attach portfolio to one or more financing plans. Its only lifecycle action is
4
+ // create; reports use the declared report request/read roles, not a money action.
5
+ // Aging selects active facilities and includes earned profit when deciding arrears.
6
+ // The default-rate report measures period-end stock, not new default incidence.
7
+ // Paid facilities are excluded and write-offs counted separately. Amounts are SAR
8
+ // minor units; declared scale and rounding govern ratios. Missing identity joins
9
+ // or unreconciled schedule counts refuse.
10
+
11
+ // Cost: portfolio.create declares no transfers, owned accounts or invocations.
12
+ // That zero instruction count does not price report capture, rendering, storage or
13
+ // release. The host owns those charges and their execution.
14
+
15
+ instrument portfolio(on: ref<financing.installments>[], default_days: integer(1, 3650) = 90, retention_years: integer(1, 100) = 10, aging_first_days: integer(1, 3650) = 30, aging_second_days: integer(1, 3650) = 60, aging_third_days: integer(1, 3650) = 90, ratio_scale: integer(1, 1000000) = 10000, ratio_rounding: enum(floor, halfUp) = floor, lock_wait_ms: integer(1, 2000) = 2000, capture_ms: integer(1, 10000) = 10000, max_rows: integer(1, 100000) = 10000, max_join_rows: integer(1, 1000000) = 100000, max_bytes: integer(1024, 16777216) = 8388608) {
16
+ summary: "Declared financing reports evaluated over frozen Product facts."
17
+ fields {}
18
+ lifecycle { states: [declared], initial: declared }
19
+ action create {}
20
+ constraints { aging_first_days: less_than(aging_second_days), aging_second_days: less_than(aging_third_days) }
21
+ reports: [
22
+ {
23
+ identity: { id: "portfolio_aging", version: 1, description: "Period-end outstanding principal by oldest unpaid installment. Remaining scheduled profit is distinct from accrued profit. Restructuring evidence is unavailable." },
24
+ scope: { kind: "product", classification: "internal", currency: "SAR" },
25
+ datasets: [
26
+ {
27
+ id: "facilities",
28
+ source: "instrument",
29
+ instruments: on,
30
+ rowKey: "facilityId",
31
+ columns: [
32
+ {
33
+ name: "facilityId",
34
+ field: "id",
35
+ type: { kind: "text" },
36
+ required: true
37
+ },
38
+ {
39
+ name: "borrowerAccount",
40
+ field: "borrower",
41
+ type: { kind: "text" },
42
+ required: true
43
+ },
44
+ {
45
+ name: "state",
46
+ field: "status",
47
+ type: { kind: "text" },
48
+ required: true
49
+ },
50
+ {
51
+ name: "originalPrincipal",
52
+ field: "principal",
53
+ type: { kind: "money", currency: "SAR" },
54
+ required: true
55
+ },
56
+ {
57
+ name: "scheduleCount",
58
+ field: "months",
59
+ type: { kind: "integer" },
60
+ required: true
61
+ },
62
+ {
63
+ name: "recordedCreation",
64
+ field: "createdAt",
65
+ type: { kind: "date" },
66
+ required: true
67
+ }
68
+ ],
69
+ expressions: [
70
+ {
71
+ id: "activeState",
72
+ op: "literal",
73
+ type: { kind: "text" },
74
+ value: "active"
75
+ },
76
+ {
77
+ id: "paidState",
78
+ op: "literal",
79
+ type: { kind: "text" },
80
+ value: "paid"
81
+ },
82
+ {
83
+ id: "writtenState",
84
+ op: "literal",
85
+ type: { kind: "text" },
86
+ value: "written_off"
87
+ },
88
+ { id: "active", op: "equal", left: "state", right: "activeState" },
89
+ { id: "paid", op: "equal", left: "state", right: "paidState" },
90
+ { id: "writtenOff", op: "equal", left: "state", right: "writtenState" },
91
+ { id: "activeOrPaid", op: "or", left: "active", right: "paid" },
92
+ { id: "financed", op: "or", left: "active", right: "writtenOff" }
93
+ ],
94
+ selection: "active"
95
+ },
96
+ {
97
+ id: "schedule",
98
+ source: "instrument",
99
+ instruments: child(on, slice),
100
+ rowKey: "sliceId",
101
+ columns: [
102
+ {
103
+ name: "sliceId",
104
+ field: "id",
105
+ type: { kind: "text" },
106
+ required: true
107
+ },
108
+ {
109
+ name: "planId",
110
+ field: "plan",
111
+ type: { kind: "text" },
112
+ required: true
113
+ },
114
+ {
115
+ name: "due",
116
+ field: "dueAt",
117
+ type: { kind: "date" },
118
+ required: true
119
+ },
120
+ {
121
+ name: "principal",
122
+ field: "principalReceivable.balance",
123
+ type: { kind: "money", currency: "SAR" },
124
+ required: true
125
+ },
126
+ {
127
+ name: "profit",
128
+ field: "profitReceivable.balance",
129
+ type: { kind: "money", currency: "SAR" },
130
+ required: true
131
+ },
132
+ {
133
+ name: "earnedProfit",
134
+ field: "profitEarned.balance",
135
+ type: { kind: "money", currency: "SAR" },
136
+ required: true
137
+ },
138
+ {
139
+ name: "scheduledPrincipal",
140
+ field: "principal",
141
+ type: { kind: "money", currency: "SAR" },
142
+ required: true
143
+ }
144
+ ],
145
+ expressions: [
146
+ {
147
+ id: "zeroMoney",
148
+ op: "literal",
149
+ type: { kind: "money", currency: "SAR" },
150
+ value: "0"
151
+ },
152
+ {
153
+ id: "zeroDays",
154
+ op: "literal",
155
+ type: { kind: "integer" },
156
+ value: 0
157
+ },
158
+ { id: "observed", op: "parameter", name: "observationAt" },
159
+ { id: "scheduledOutstanding", op: "sum", left: "principal", right: "profit" },
160
+ { id: "totalOutstanding", op: "sum", left: "scheduledOutstanding", right: "earnedProfit" },
161
+ { id: "unpaid", op: "less", left: "zeroMoney", right: "totalOutstanding" },
162
+ { id: "elapsed", op: "daysBetween", left: "due", right: "observed" },
163
+ { id: "nonnegativeDays", op: "maximum", left: "elapsed", right: "zeroDays" },
164
+ { id: "daysPastDue", op: "choose", condition: "unpaid", yes: "nonnegativeDays", no: "zeroDays" }
165
+ ]
166
+ },
167
+ {
168
+ id: "borrowerAccounts",
169
+ source: "account",
170
+ instruments: [ ],
171
+ rowKey: "accountId",
172
+ columns: [
173
+ {
174
+ name: "accountId",
175
+ field: "id",
176
+ type: { kind: "text" },
177
+ required: true
178
+ },
179
+ {
180
+ name: "participant",
181
+ field: "ownerParticipantId",
182
+ type: { kind: "text" },
183
+ required: false
184
+ }
185
+ ],
186
+ expressions: [ ]
187
+ },
188
+ {
189
+ id: "borrowers",
190
+ source: "identity",
191
+ instruments: [ ],
192
+ rowKey: "customerId",
193
+ columns: [
194
+ {
195
+ name: "customerId",
196
+ field: "id",
197
+ type: { kind: "text" },
198
+ required: true
199
+ },
200
+ {
201
+ name: "participantId",
202
+ field: "participantId",
203
+ type: { kind: "text" },
204
+ required: true
205
+ },
206
+ {
207
+ name: "entityId",
208
+ field: "entityId",
209
+ type: { kind: "text" },
210
+ required: true
211
+ }
212
+ ],
213
+ expressions: [ ]
214
+ }
215
+ ],
216
+ time: { timezone: "Asia/Riyadh", boundary: "startInclusiveEndExclusive", observation: "periodEnd", history: "retainedOnly" },
217
+ selection: { dataset: "facilities" },
218
+ calculation: {
219
+ joins: [
220
+ {
221
+ dataset: "schedule",
222
+ local: "facilityId",
223
+ foreign: "planId",
224
+ cardinality: "many",
225
+ missing: "refuse",
226
+ aggregates: [
227
+ { name: "outstandingPrincipal", op: "sum", value: "principal" },
228
+ { name: "remainingScheduledProfit", op: "sum", value: "profit" },
229
+ { name: "scheduledPrincipalTotal", op: "sum", value: "scheduledPrincipal" },
230
+ { name: "capturedScheduleCount", op: "count" },
231
+ { name: "oldestUnpaidDays", op: "maximum", value: "daysPastDue" }
232
+ ]
233
+ },
234
+ {
235
+ dataset: "borrowerAccounts",
236
+ local: "borrowerAccount",
237
+ foreign: "accountId",
238
+ cardinality: "one",
239
+ missing: "refuse",
240
+ aggregates: [
241
+ { name: "borrowerParticipant", op: "minimum", value: "participant" }
242
+ ]
243
+ },
244
+ {
245
+ dataset: "borrowers",
246
+ local: "borrowerParticipant",
247
+ foreign: "participantId",
248
+ cardinality: "one",
249
+ missing: "refuse",
250
+ aggregates: [
251
+ { name: "borrowerEntity", op: "minimum", value: "entityId" }
252
+ ]
253
+ }
254
+ ],
255
+ expressions: [
256
+ {
257
+ id: "day0",
258
+ op: "literal",
259
+ type: { kind: "integer" },
260
+ value: 0
261
+ },
262
+ {
263
+ id: "day30",
264
+ op: "literal",
265
+ type: { kind: "integer" },
266
+ value: aging_first_days
267
+ },
268
+ {
269
+ id: "day60",
270
+ op: "literal",
271
+ type: { kind: "integer" },
272
+ value: aging_second_days
273
+ },
274
+ {
275
+ id: "day90",
276
+ op: "literal",
277
+ type: { kind: "integer" },
278
+ value: aging_third_days
279
+ },
280
+ {
281
+ id: "band0",
282
+ op: "literal",
283
+ type: { kind: "text" },
284
+ value: "current"
285
+ },
286
+ {
287
+ id: "band30",
288
+ op: "literal",
289
+ type: { kind: "text" },
290
+ value: "first"
291
+ },
292
+ {
293
+ id: "band60",
294
+ op: "literal",
295
+ type: { kind: "text" },
296
+ value: "second"
297
+ },
298
+ {
299
+ id: "band90",
300
+ op: "literal",
301
+ type: { kind: "text" },
302
+ value: "third"
303
+ },
304
+ {
305
+ id: "bandLate",
306
+ op: "literal",
307
+ type: { kind: "text" },
308
+ value: "later"
309
+ },
310
+ { id: "current", op: "atMost", left: "oldestUnpaidDays", right: "day0" },
311
+ { id: "first", op: "atMost", left: "oldestUnpaidDays", right: "day30" },
312
+ { id: "second", op: "atMost", left: "oldestUnpaidDays", right: "day60" },
313
+ { id: "third", op: "atMost", left: "oldestUnpaidDays", right: "day90" },
314
+ { id: "lateBand", op: "choose", condition: "third", yes: "band90", no: "bandLate" },
315
+ { id: "secondBand", op: "choose", condition: "second", yes: "band60", no: "lateBand" },
316
+ { id: "firstBand", op: "choose", condition: "first", yes: "band30", no: "secondBand" },
317
+ { id: "agingBand", op: "choose", condition: "current", yes: "band0", no: "firstBand" }
318
+ ],
319
+ groupBy: [ "agingBand" ],
320
+ aggregates: [
321
+ { name: "facilities", op: "count" },
322
+ { name: "principalMinor", op: "sum", value: "outstandingPrincipal" },
323
+ { name: "scheduledProfitMinor", op: "sum", value: "remainingScheduledProfit" }
324
+ ],
325
+ resultExpressions: [ ]
326
+ },
327
+ validation: {
328
+ empty: "noEligibleFacilities",
329
+ required: [ "borrowerEntity" ],
330
+ reconcile: [ ],
331
+ rowReconcile: [
332
+ { left: "originalPrincipal", right: "scheduledPrincipalTotal" },
333
+ { left: "scheduleCount", right: "capturedScheduleCount" }
334
+ ],
335
+ unavailableFacts: [ ],
336
+ lockTimeoutMs: lock_wait_ms,
337
+ captureTimeoutMs: capture_ms,
338
+ maxRows: max_rows,
339
+ maxJoinRows: max_join_rows,
340
+ maxBytes: max_bytes
341
+ },
342
+ output: {
343
+ profile: "internal.v1",
344
+ formats: [ "json", "csv" ],
345
+ columns: [
346
+ {
347
+ name: "agingBand",
348
+ label: "agingBand",
349
+ type: { kind: "text" }
350
+ },
351
+ {
352
+ name: "facilities",
353
+ label: "facilities",
354
+ type: { kind: "integer" }
355
+ },
356
+ {
357
+ name: "principalMinor",
358
+ label: "principalMinor",
359
+ type: { kind: "money", currency: "SAR" }
360
+ },
361
+ {
362
+ name: "scheduledProfitMinor",
363
+ label: "scheduledProfitMinor",
364
+ type: { kind: "money", currency: "SAR" }
365
+ }
366
+ ],
367
+ sort: [ "agingBand" ]
368
+ },
369
+ authority: {
370
+ request: [ "owner", "finance" ],
371
+ read: [ "owner", "admin", "operations", "finance", "viewer" ],
372
+ release: [ ],
373
+ retention: { policy: "report_replay", years: retention_years }
374
+ }
375
+ },
376
+ {
377
+ identity: { id: "period_end_outstanding_principal_default_rate", version: 1, description: "Current active facilities with any unpaid installment at least the declared default days past due, observed at period end. Cure when arrears clear. Count and outstanding-principal weighted rates in the declared scale exclude paid and written-off facilities; write-offs are counted separately. This is a stock measure, not a new-default incidence or origination cohort rate." },
378
+ scope: { kind: "product", classification: "internal", currency: "SAR" },
379
+ datasets: [
380
+ {
381
+ id: "facilities",
382
+ source: "instrument",
383
+ instruments: on,
384
+ rowKey: "facilityId",
385
+ columns: [
386
+ {
387
+ name: "facilityId",
388
+ field: "id",
389
+ type: { kind: "text" },
390
+ required: true
391
+ },
392
+ {
393
+ name: "borrowerAccount",
394
+ field: "borrower",
395
+ type: { kind: "text" },
396
+ required: true
397
+ },
398
+ {
399
+ name: "state",
400
+ field: "status",
401
+ type: { kind: "text" },
402
+ required: true
403
+ },
404
+ {
405
+ name: "originalPrincipal",
406
+ field: "principal",
407
+ type: { kind: "money", currency: "SAR" },
408
+ required: true
409
+ },
410
+ {
411
+ name: "scheduleCount",
412
+ field: "months",
413
+ type: { kind: "integer" },
414
+ required: true
415
+ },
416
+ {
417
+ name: "recordedCreation",
418
+ field: "createdAt",
419
+ type: { kind: "date" },
420
+ required: true
421
+ }
422
+ ],
423
+ expressions: [
424
+ {
425
+ id: "activeState",
426
+ op: "literal",
427
+ type: { kind: "text" },
428
+ value: "active"
429
+ },
430
+ {
431
+ id: "paidState",
432
+ op: "literal",
433
+ type: { kind: "text" },
434
+ value: "paid"
435
+ },
436
+ {
437
+ id: "writtenState",
438
+ op: "literal",
439
+ type: { kind: "text" },
440
+ value: "written_off"
441
+ },
442
+ { id: "active", op: "equal", left: "state", right: "activeState" },
443
+ { id: "paid", op: "equal", left: "state", right: "paidState" },
444
+ { id: "writtenOff", op: "equal", left: "state", right: "writtenState" },
445
+ { id: "activeOrPaid", op: "or", left: "active", right: "paid" },
446
+ { id: "financed", op: "or", left: "active", right: "writtenOff" }
447
+ ],
448
+ selection: "financed"
449
+ },
450
+ {
451
+ id: "schedule",
452
+ source: "instrument",
453
+ instruments: child(on, slice),
454
+ rowKey: "sliceId",
455
+ columns: [
456
+ {
457
+ name: "sliceId",
458
+ field: "id",
459
+ type: { kind: "text" },
460
+ required: true
461
+ },
462
+ {
463
+ name: "planId",
464
+ field: "plan",
465
+ type: { kind: "text" },
466
+ required: true
467
+ },
468
+ {
469
+ name: "due",
470
+ field: "dueAt",
471
+ type: { kind: "date" },
472
+ required: true
473
+ },
474
+ {
475
+ name: "principal",
476
+ field: "principalReceivable.balance",
477
+ type: { kind: "money", currency: "SAR" },
478
+ required: true
479
+ },
480
+ {
481
+ name: "profit",
482
+ field: "profitReceivable.balance",
483
+ type: { kind: "money", currency: "SAR" },
484
+ required: true
485
+ },
486
+ {
487
+ name: "earnedProfit",
488
+ field: "profitEarned.balance",
489
+ type: { kind: "money", currency: "SAR" },
490
+ required: true
491
+ },
492
+ {
493
+ name: "scheduledPrincipal",
494
+ field: "principal",
495
+ type: { kind: "money", currency: "SAR" },
496
+ required: true
497
+ }
498
+ ],
499
+ expressions: [
500
+ {
501
+ id: "zeroMoney",
502
+ op: "literal",
503
+ type: { kind: "money", currency: "SAR" },
504
+ value: "0"
505
+ },
506
+ {
507
+ id: "zeroDays",
508
+ op: "literal",
509
+ type: { kind: "integer" },
510
+ value: 0
511
+ },
512
+ { id: "observed", op: "parameter", name: "observationAt" },
513
+ { id: "scheduledOutstanding", op: "sum", left: "principal", right: "profit" },
514
+ { id: "totalOutstanding", op: "sum", left: "scheduledOutstanding", right: "earnedProfit" },
515
+ { id: "unpaid", op: "less", left: "zeroMoney", right: "totalOutstanding" },
516
+ { id: "elapsed", op: "daysBetween", left: "due", right: "observed" },
517
+ { id: "nonnegativeDays", op: "maximum", left: "elapsed", right: "zeroDays" },
518
+ { id: "daysPastDue", op: "choose", condition: "unpaid", yes: "nonnegativeDays", no: "zeroDays" }
519
+ ]
520
+ },
521
+ {
522
+ id: "borrowerAccounts",
523
+ source: "account",
524
+ instruments: [ ],
525
+ rowKey: "accountId",
526
+ columns: [
527
+ {
528
+ name: "accountId",
529
+ field: "id",
530
+ type: { kind: "text" },
531
+ required: true
532
+ },
533
+ {
534
+ name: "participant",
535
+ field: "ownerParticipantId",
536
+ type: { kind: "text" },
537
+ required: false
538
+ }
539
+ ],
540
+ expressions: [ ]
541
+ },
542
+ {
543
+ id: "borrowers",
544
+ source: "identity",
545
+ instruments: [ ],
546
+ rowKey: "customerId",
547
+ columns: [
548
+ {
549
+ name: "customerId",
550
+ field: "id",
551
+ type: { kind: "text" },
552
+ required: true
553
+ },
554
+ {
555
+ name: "participantId",
556
+ field: "participantId",
557
+ type: { kind: "text" },
558
+ required: true
559
+ },
560
+ {
561
+ name: "entityId",
562
+ field: "entityId",
563
+ type: { kind: "text" },
564
+ required: true
565
+ }
566
+ ],
567
+ expressions: [ ]
568
+ }
569
+ ],
570
+ time: { timezone: "Asia/Riyadh", boundary: "startInclusiveEndExclusive", observation: "periodEnd", history: "retainedOnly" },
571
+ selection: { dataset: "facilities" },
572
+ calculation: {
573
+ joins: [
574
+ {
575
+ dataset: "schedule",
576
+ local: "facilityId",
577
+ foreign: "planId",
578
+ cardinality: "many",
579
+ missing: "refuse",
580
+ aggregates: [
581
+ { name: "outstandingPrincipal", op: "sum", value: "principal" },
582
+ { name: "remainingScheduledProfit", op: "sum", value: "profit" },
583
+ { name: "scheduledPrincipalTotal", op: "sum", value: "scheduledPrincipal" },
584
+ { name: "capturedScheduleCount", op: "count" },
585
+ { name: "oldestUnpaidDays", op: "maximum", value: "daysPastDue" }
586
+ ]
587
+ },
588
+ {
589
+ dataset: "borrowerAccounts",
590
+ local: "borrowerAccount",
591
+ foreign: "accountId",
592
+ cardinality: "one",
593
+ missing: "refuse",
594
+ aggregates: [
595
+ { name: "borrowerParticipant", op: "minimum", value: "participant" }
596
+ ]
597
+ },
598
+ {
599
+ dataset: "borrowers",
600
+ local: "borrowerParticipant",
601
+ foreign: "participantId",
602
+ cardinality: "one",
603
+ missing: "refuse",
604
+ aggregates: [
605
+ { name: "borrowerEntity", op: "minimum", value: "entityId" }
606
+ ]
607
+ }
608
+ ],
609
+ expressions: [
610
+ {
611
+ id: "threshold",
612
+ op: "literal",
613
+ type: { kind: "integer" },
614
+ value: default_days
615
+ },
616
+ {
617
+ id: "zero",
618
+ op: "literal",
619
+ type: { kind: "integer" },
620
+ value: 0
621
+ },
622
+ {
623
+ id: "one",
624
+ op: "literal",
625
+ type: { kind: "integer" },
626
+ value: 1
627
+ },
628
+ {
629
+ id: "zeroMoney",
630
+ op: "literal",
631
+ type: { kind: "money", currency: "SAR" },
632
+ value: "0"
633
+ },
634
+ { id: "pastThreshold", op: "atMost", left: "threshold", right: "oldestUnpaidDays" },
635
+ { id: "isDefault", op: "and", left: "active", right: "pastThreshold" },
636
+ { id: "activeCount", op: "choose", condition: "active", yes: "one", no: "zero" },
637
+ { id: "defaultCount", op: "choose", condition: "isDefault", yes: "one", no: "zero" },
638
+ { id: "writeOffCount", op: "choose", condition: "writtenOff", yes: "one", no: "zero" },
639
+ { id: "activePrincipal", op: "choose", condition: "active", yes: "outstandingPrincipal", no: "zeroMoney" },
640
+ { id: "defaultPrincipal", op: "choose", condition: "isDefault", yes: "outstandingPrincipal", no: "zeroMoney" }
641
+ ],
642
+ groupBy: [ ],
643
+ aggregates: [
644
+ { name: "activeFacilities", op: "sum", value: "activeCount" },
645
+ { name: "defaultFacilities", op: "sum", value: "defaultCount" },
646
+ { name: "writtenOffFacilities", op: "sum", value: "writeOffCount" },
647
+ { name: "activePrincipalMinor", op: "sum", value: "activePrincipal" },
648
+ { name: "defaultPrincipalMinor", op: "sum", value: "defaultPrincipal" }
649
+ ],
650
+ resultExpressions: [
651
+ { id: "countRate", op: "ratio", numerator: "defaultFacilities", denominator: "activeFacilities", scale: ratio_scale, rounding: ratio_rounding, zero: "null" },
652
+ { id: "principalRate", op: "ratio", numerator: "defaultPrincipalMinor", denominator: "activePrincipalMinor", scale: ratio_scale, rounding: ratio_rounding, zero: "null" },
653
+ {
654
+ id: "restructuringEvidence",
655
+ op: "literal",
656
+ type: { kind: "text" },
657
+ value: "unavailable"
658
+ },
659
+ {
660
+ id: "defaultDays",
661
+ op: "literal",
662
+ type: { kind: "integer" },
663
+ value: default_days
664
+ },
665
+ {
666
+ id: "rateScale",
667
+ op: "literal",
668
+ type: { kind: "integer" },
669
+ value: ratio_scale
670
+ }
671
+ ]
672
+ },
673
+ validation: {
674
+ empty: "refuse",
675
+ required: [ "borrowerEntity" ],
676
+ reconcile: [ ],
677
+ rowReconcile: [
678
+ { left: "originalPrincipal", right: "scheduledPrincipalTotal" },
679
+ { left: "scheduleCount", right: "capturedScheduleCount" }
680
+ ],
681
+ unavailableFacts: [ ],
682
+ lockTimeoutMs: lock_wait_ms,
683
+ captureTimeoutMs: capture_ms,
684
+ maxRows: max_rows,
685
+ maxJoinRows: max_join_rows,
686
+ maxBytes: max_bytes
687
+ },
688
+ output: {
689
+ profile: "internal.v1",
690
+ formats: [ "json", "csv" ],
691
+ columns: [
692
+ {
693
+ name: "activeFacilities",
694
+ label: "activeFacilities",
695
+ type: { kind: "integer" }
696
+ },
697
+ {
698
+ name: "defaultFacilities",
699
+ label: "defaultFacilities",
700
+ type: { kind: "integer" }
701
+ },
702
+ {
703
+ name: "writtenOffFacilities",
704
+ label: "writtenOffFacilities",
705
+ type: { kind: "integer" }
706
+ },
707
+ {
708
+ name: "activePrincipalMinor",
709
+ label: "activePrincipalMinor",
710
+ type: { kind: "money", currency: "SAR" }
711
+ },
712
+ {
713
+ name: "defaultPrincipalMinor",
714
+ label: "defaultPrincipalMinor",
715
+ type: { kind: "money", currency: "SAR" }
716
+ },
717
+ {
718
+ name: "countRate",
719
+ label: "countRate",
720
+ type: { kind: "integer" }
721
+ },
722
+ {
723
+ name: "principalRate",
724
+ label: "principalRate",
725
+ type: { kind: "integer" }
726
+ },
727
+ {
728
+ name: "restructuringEvidence",
729
+ label: "restructuringEvidence",
730
+ type: { kind: "text" }
731
+ },
732
+ {
733
+ name: "defaultDays",
734
+ label: "defaultDays",
735
+ type: { kind: "integer" }
736
+ },
737
+ {
738
+ name: "rateScale",
739
+ label: "rateScale",
740
+ type: { kind: "integer" }
741
+ }
742
+ ],
743
+ sort: [ "activeFacilities" ]
744
+ },
745
+ authority: {
746
+ request: [ "owner", "finance" ],
747
+ read: [ "owner", "admin", "operations", "finance", "viewer" ],
748
+ release: [ ],
749
+ retention: { policy: "report_replay", years: retention_years }
750
+ }
751
+ }
752
+ ]
753
+ }