business-as-code 2.1.3 → 2.4.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 (235) hide show
  1. package/.turbo/turbo-build.log +4 -5
  2. package/CHANGELOG.md +53 -0
  3. package/README.md +2 -0
  4. package/dist/dollar.d.ts.map +1 -1
  5. package/dist/dollar.js +2 -2
  6. package/dist/dollar.js.map +1 -1
  7. package/dist/entities/organization.d.ts +4 -0
  8. package/dist/entities/organization.d.ts.map +1 -1
  9. package/dist/entities/organization.js +27 -18
  10. package/dist/entities/organization.js.map +1 -1
  11. package/dist/entities/planning.d.ts +87 -0
  12. package/dist/finance/account.d.ts +44 -0
  13. package/dist/finance/account.d.ts.map +1 -0
  14. package/dist/finance/account.js +6 -0
  15. package/dist/finance/account.js.map +1 -0
  16. package/dist/finance/authority.d.ts +78 -0
  17. package/dist/finance/authority.d.ts.map +1 -0
  18. package/dist/finance/authority.js +27 -0
  19. package/dist/finance/authority.js.map +1 -0
  20. package/dist/finance/card.d.ts +36 -0
  21. package/dist/finance/card.d.ts.map +1 -0
  22. package/dist/finance/card.js +6 -0
  23. package/dist/finance/card.js.map +1 -0
  24. package/dist/finance/identity.d.ts +30 -0
  25. package/dist/finance/identity.d.ts.map +1 -0
  26. package/dist/finance/identity.js +8 -0
  27. package/dist/finance/identity.js.map +1 -0
  28. package/dist/finance/index.d.ts +36 -0
  29. package/dist/finance/index.d.ts.map +1 -0
  30. package/dist/finance/index.js +22 -0
  31. package/dist/finance/index.js.map +1 -0
  32. package/dist/finance/ledger.d.ts +24 -0
  33. package/dist/finance/ledger.d.ts.map +1 -0
  34. package/dist/finance/ledger.js +8 -0
  35. package/dist/finance/ledger.js.map +1 -0
  36. package/dist/finance/merchant.d.ts +129 -0
  37. package/dist/finance/merchant.d.ts.map +1 -0
  38. package/dist/finance/merchant.js +21 -0
  39. package/dist/finance/merchant.js.map +1 -0
  40. package/dist/finance/outcome-contract.d.ts +139 -0
  41. package/dist/finance/outcome-contract.d.ts.map +1 -0
  42. package/dist/finance/outcome-contract.js +27 -0
  43. package/dist/finance/outcome-contract.js.map +1 -0
  44. package/dist/finance/port.d.ts +121 -0
  45. package/dist/finance/port.d.ts.map +1 -0
  46. package/dist/finance/port.js +10 -0
  47. package/dist/finance/port.js.map +1 -0
  48. package/dist/finance/pricing.d.ts +154 -0
  49. package/dist/finance/pricing.d.ts.map +1 -0
  50. package/dist/finance/pricing.js +79 -0
  51. package/dist/finance/pricing.js.map +1 -0
  52. package/dist/finance/proof-predicate.d.ts +92 -0
  53. package/dist/finance/proof-predicate.d.ts.map +1 -0
  54. package/dist/finance/proof-predicate.js +80 -0
  55. package/dist/finance/proof-predicate.js.map +1 -0
  56. package/dist/finance/refund.d.ts +44 -0
  57. package/dist/finance/refund.d.ts.map +1 -0
  58. package/dist/finance/refund.js +41 -0
  59. package/dist/finance/refund.js.map +1 -0
  60. package/dist/finance/sla.d.ts +25 -0
  61. package/dist/finance/sla.d.ts.map +1 -0
  62. package/dist/finance/sla.js +7 -0
  63. package/dist/finance/sla.js.map +1 -0
  64. package/dist/finance/types.d.ts +79 -0
  65. package/dist/finance/types.d.ts.map +1 -0
  66. package/dist/finance/types.js +8 -0
  67. package/dist/{canvas → finance}/types.js.map +1 -1
  68. package/dist/goals.d.ts +19 -0
  69. package/dist/goals.d.ts.map +1 -1
  70. package/dist/goals.js +81 -12
  71. package/dist/goals.js.map +1 -1
  72. package/dist/index.d.ts +12 -8
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +19 -7
  75. package/dist/index.js.map +1 -1
  76. package/dist/kpis.d.ts +19 -0
  77. package/dist/kpis.d.ts.map +1 -1
  78. package/dist/kpis.js +71 -6
  79. package/dist/kpis.js.map +1 -1
  80. package/dist/metrics.d.ts.map +1 -1
  81. package/dist/metrics.js +29 -24
  82. package/dist/metrics.js.map +1 -1
  83. package/dist/okrs.d.ts +34 -0
  84. package/dist/okrs.d.ts.map +1 -1
  85. package/dist/okrs.js +135 -13
  86. package/dist/okrs.js.map +1 -1
  87. package/dist/organization.d.ts.map +1 -1
  88. package/dist/organization.js +11 -11
  89. package/dist/organization.js.map +1 -1
  90. package/dist/process.d.ts.map +1 -1
  91. package/dist/process.js +13 -12
  92. package/dist/process.js.map +1 -1
  93. package/dist/product.d.ts.map +1 -1
  94. package/dist/product.js +9 -9
  95. package/dist/product.js.map +1 -1
  96. package/dist/queries.d.ts.map +1 -1
  97. package/dist/queries.js +194 -32
  98. package/dist/queries.js.map +1 -1
  99. package/dist/roles.d.ts +25 -31
  100. package/dist/roles.d.ts.map +1 -1
  101. package/dist/roles.js +37 -10
  102. package/dist/roles.js.map +1 -1
  103. package/dist/workflow.d.ts.map +1 -1
  104. package/dist/workflow.js +13 -12
  105. package/dist/workflow.js.map +1 -1
  106. package/package.json +20 -13
  107. package/src/dollar.ts +5 -2
  108. package/src/entities/organization.ts +31 -18
  109. package/src/finance/account.ts +48 -0
  110. package/src/finance/authority.ts +42 -0
  111. package/src/finance/card.ts +38 -0
  112. package/src/finance/identity.ts +31 -0
  113. package/src/finance/index.ts +117 -0
  114. package/src/finance/ledger.ts +26 -0
  115. package/src/finance/merchant.ts +127 -0
  116. package/src/finance/outcome-contract.ts +157 -0
  117. package/src/finance/port.ts +144 -0
  118. package/src/finance/pricing.ts +197 -0
  119. package/src/finance/proof-predicate.ts +106 -0
  120. package/src/finance/refund.ts +52 -0
  121. package/src/finance/sla.ts +33 -0
  122. package/src/finance/types.ts +75 -0
  123. package/src/goals.ts +78 -12
  124. package/src/index.ts +48 -18
  125. package/src/kpis.ts +62 -8
  126. package/src/metrics.ts +92 -79
  127. package/src/okrs.ts +120 -20
  128. package/src/organization.ts +12 -15
  129. package/src/process.ts +11 -12
  130. package/src/product.ts +8 -9
  131. package/src/queries.ts +238 -75
  132. package/src/roles.ts +62 -61
  133. package/src/workflow.ts +22 -15
  134. package/test/business.test.ts +282 -0
  135. package/test/dollar.test.ts +270 -0
  136. package/test/entities.test.ts +628 -0
  137. package/test/financials.test.ts +539 -0
  138. package/test/goals.test.ts +451 -0
  139. package/{src → test}/index.test.ts +1 -1
  140. package/test/kpis.test.ts +440 -0
  141. package/test/metrics.test.ts +744 -0
  142. package/test/okrs.test.ts +741 -0
  143. package/test/organization.test.ts +548 -0
  144. package/test/process.test.ts +503 -0
  145. package/test/product.test.ts +430 -0
  146. package/test/queries.test.ts +556 -0
  147. package/test/roles.test.ts +546 -0
  148. package/test/service.test.ts +450 -0
  149. package/test/types.test.ts +1141 -0
  150. package/test/vision.test.ts +214 -0
  151. package/test/workflow.test.ts +501 -0
  152. package/vitest.config.ts +47 -0
  153. package/LICENSE +0 -21
  154. package/dist/canvas/activities.d.ts +0 -19
  155. package/dist/canvas/activities.d.ts.map +0 -1
  156. package/dist/canvas/activities.js +0 -20
  157. package/dist/canvas/activities.js.map +0 -1
  158. package/dist/canvas/channels.d.ts +0 -20
  159. package/dist/canvas/channels.d.ts.map +0 -1
  160. package/dist/canvas/channels.js +0 -21
  161. package/dist/canvas/channels.js.map +0 -1
  162. package/dist/canvas/relationships.d.ts +0 -20
  163. package/dist/canvas/relationships.d.ts.map +0 -1
  164. package/dist/canvas/relationships.js +0 -21
  165. package/dist/canvas/relationships.js.map +0 -1
  166. package/dist/canvas/resources.d.ts +0 -20
  167. package/dist/canvas/resources.d.ts.map +0 -1
  168. package/dist/canvas/resources.js +0 -30
  169. package/dist/canvas/resources.js.map +0 -1
  170. package/dist/canvas/revenue.d.ts +0 -22
  171. package/dist/canvas/revenue.d.ts.map +0 -1
  172. package/dist/canvas/revenue.js +0 -30
  173. package/dist/canvas/revenue.js.map +0 -1
  174. package/dist/canvas/segments.d.ts +0 -20
  175. package/dist/canvas/segments.d.ts.map +0 -1
  176. package/dist/canvas/segments.js +0 -28
  177. package/dist/canvas/segments.js.map +0 -1
  178. package/dist/canvas/types.d.ts +0 -232
  179. package/dist/canvas/types.d.ts.map +0 -1
  180. package/dist/canvas/types.js +0 -8
  181. package/dist/canvas/value.d.ts +0 -20
  182. package/dist/canvas/value.d.ts.map +0 -1
  183. package/dist/canvas/value.js +0 -21
  184. package/dist/canvas/value.js.map +0 -1
  185. package/src/business.js +0 -108
  186. package/src/canvas/activities.ts +0 -32
  187. package/src/canvas/canvas.ts +0 -482
  188. package/src/canvas/channels.ts +0 -34
  189. package/src/canvas/costs.ts +0 -43
  190. package/src/canvas/economics.ts +0 -99
  191. package/src/canvas/index.ts +0 -206
  192. package/src/canvas/partnerships.ts +0 -34
  193. package/src/canvas/projections.ts +0 -141
  194. package/src/canvas/relationships.ts +0 -34
  195. package/src/canvas/resources.ts +0 -43
  196. package/src/canvas/revenue.ts +0 -56
  197. package/src/canvas/segments.ts +0 -42
  198. package/src/canvas/types.ts +0 -363
  199. package/src/canvas/value.ts +0 -34
  200. package/src/dollar.js +0 -106
  201. package/src/entities/assets.js +0 -322
  202. package/src/entities/business.js +0 -369
  203. package/src/entities/communication.js +0 -254
  204. package/src/entities/customers.js +0 -988
  205. package/src/entities/financials.js +0 -931
  206. package/src/entities/goals.js +0 -799
  207. package/src/entities/index.js +0 -197
  208. package/src/entities/legal.js +0 -300
  209. package/src/entities/market.js +0 -300
  210. package/src/entities/marketing.js +0 -1156
  211. package/src/entities/offerings.js +0 -726
  212. package/src/entities/operations.js +0 -786
  213. package/src/entities/organization.js +0 -806
  214. package/src/entities/partnerships.js +0 -299
  215. package/src/entities/planning.js +0 -270
  216. package/src/entities/projects.js +0 -348
  217. package/src/entities/risk.js +0 -292
  218. package/src/entities/sales.js +0 -1247
  219. package/src/financials.js +0 -296
  220. package/src/goals.js +0 -214
  221. package/src/index.js +0 -131
  222. package/src/index.test.js +0 -274
  223. package/src/kpis.js +0 -231
  224. package/src/metrics.js +0 -324
  225. package/src/okrs.js +0 -268
  226. package/src/organization.js +0 -172
  227. package/src/process.js +0 -240
  228. package/src/product.js +0 -144
  229. package/src/queries.js +0 -414
  230. package/src/roles.js +0 -254
  231. package/src/service.js +0 -139
  232. package/src/types.js +0 -4
  233. package/src/vision.js +0 -67
  234. package/src/workflow.js +0 -246
  235. package/tests/canvas.test.ts +0 -842
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Pricing — discriminated union with five factory variants.
3
+ *
4
+ * outcome — pay on delivery; tiers by complexity (S/M/L)
5
+ * subscription — recurring plan + optional metered overage
6
+ * perInvocation — flat per-call with included-tier ladder
7
+ * composite — one-time base + metered events
8
+ * percent-of — proportional charge against a realised basis
9
+ * (invoice amount, collected amount, transaction volume)
10
+ * with optional cap / floor
11
+ *
12
+ * Each factory returns a typed Pricing value with discriminator on .kind.
13
+ */
14
+
15
+ import type { Currency, Money } from './types.js'
16
+ import type { SLATarget } from './sla.js'
17
+
18
+ export interface OutcomeTier {
19
+ id: string
20
+ amount: bigint
21
+ currency?: Currency
22
+ description?: string
23
+ }
24
+
25
+ export interface PerInvocationTier {
26
+ id: string
27
+ amount: bigint
28
+ /** Number of invocations included before per-tier billing applies. */
29
+ includedPerMonth?: number
30
+ /** Per-invocation overage cost above included. */
31
+ overage?: bigint
32
+ }
33
+
34
+ /**
35
+ * A single metered-billing line item. `description` is genuinely optional —
36
+ * callers may omit it from inline literals AND from the {@link Pricing}
37
+ * factory calls (`Pricing.subscription` / `Pricing.composite`).
38
+ */
39
+ export interface MeteredEntry {
40
+ event: string
41
+ amount: bigint
42
+ description?: string
43
+ }
44
+
45
+ /**
46
+ * Optional one-time base charge on a {@link Pricing.composite} plan. `description`
47
+ * is optional under `exactOptionalPropertyTypes`.
48
+ */
49
+ export interface CompositeBase {
50
+ id: string
51
+ amount: bigint
52
+ description?: string
53
+ }
54
+
55
+ /**
56
+ * Recurring plan portion of a {@link Pricing.subscription}. Shared between the
57
+ * `Pricing` discriminated union and the {@link Pricing.subscription} factory
58
+ * so the two shapes never drift.
59
+ */
60
+ export interface SubscriptionPlan {
61
+ id: string
62
+ amount: bigint
63
+ currency: Currency
64
+ interval: 'day' | 'week' | 'month' | 'quarter' | 'year'
65
+ }
66
+
67
+ /**
68
+ * Standard bases the {@link Pricing.percentOf} runtime knows how to resolve
69
+ * at settlement time. Adapters MAY accept arbitrary basis strings for
70
+ * domain-specific metering, but the canonical four cover the common cases:
71
+ *
72
+ * invoice-amount — face value of an outbound invoice
73
+ * collected-amount — funds actually received (post-settlement)
74
+ * transaction-volume — gross payment volume processed
75
+ * <custom string> — provider-defined; must be resolvable in the
76
+ * metering runtime
77
+ */
78
+ export type PercentOfBasis =
79
+ | 'invoice-amount'
80
+ | 'collected-amount'
81
+ | 'transaction-volume'
82
+ | (string & {})
83
+
84
+ export type Pricing =
85
+ | {
86
+ kind: 'outcome'
87
+ tiers: OutcomeTier[]
88
+ sla?: SLATarget
89
+ }
90
+ | {
91
+ kind: 'subscription'
92
+ plan: SubscriptionPlan
93
+ metered?: MeteredEntry[]
94
+ sla?: SLATarget
95
+ }
96
+ | {
97
+ kind: 'per-invocation'
98
+ tiers: PerInvocationTier[]
99
+ }
100
+ | {
101
+ kind: 'composite'
102
+ base: CompositeBase
103
+ metered: MeteredEntry[]
104
+ }
105
+ | {
106
+ kind: 'percent-of'
107
+ basis: PercentOfBasis
108
+ /**
109
+ * Rate in basis points (1/100ths of a percent). Examples: `200` = 2%,
110
+ * `75` = 0.75%, `1000` = 10%.
111
+ *
112
+ * The metering runtime computes the charge as
113
+ * `(realised_basis * rateBasisPoints) / 10000`, then clamps the
114
+ * result by the optional `cap` / `floor` (when present).
115
+ */
116
+ rateBasisPoints: number
117
+ /** Optional upper bound on the per-event charge. */
118
+ cap?: Money
119
+ /** Optional lower bound on the per-event charge. */
120
+ floor?: Money
121
+ }
122
+
123
+ export const Pricing = {
124
+ outcome(opts: { tiers: OutcomeTier[]; sla?: SLATarget }): Pricing {
125
+ if (opts.sla !== undefined) {
126
+ return { kind: 'outcome', tiers: opts.tiers, sla: opts.sla }
127
+ }
128
+ return { kind: 'outcome', tiers: opts.tiers }
129
+ },
130
+
131
+ subscription(opts: {
132
+ plan: SubscriptionPlan
133
+ metered?: MeteredEntry[]
134
+ sla?: SLATarget
135
+ }): Pricing {
136
+ const result: Extract<Pricing, { kind: 'subscription' }> = {
137
+ kind: 'subscription',
138
+ plan: opts.plan,
139
+ }
140
+ if (opts.metered !== undefined) result.metered = opts.metered
141
+ if (opts.sla !== undefined) result.sla = opts.sla
142
+ return result
143
+ },
144
+
145
+ perInvocation(opts: { tiers: PerInvocationTier[] }): Pricing {
146
+ return { kind: 'per-invocation', tiers: opts.tiers }
147
+ },
148
+
149
+ composite(opts: { base: CompositeBase; metered: MeteredEntry[] }): Pricing {
150
+ return { kind: 'composite', base: opts.base, metered: opts.metered }
151
+ },
152
+
153
+ /**
154
+ * Percent-of-basis pricing — proportional charge against a realised
155
+ * basis (e.g. invoice amount, collected amount, transaction volume).
156
+ *
157
+ * The metering runtime resolves `basis` to a concrete bigint at
158
+ * settlement time, then computes the charge as
159
+ * `(realised_basis * rateBasisPoints) / 10000`, optionally clamped by
160
+ * `cap` / `floor`.
161
+ *
162
+ * @example AR Service: 2% of collected funds
163
+ * ```ts
164
+ * Pricing.percentOf({ basis: 'collected-amount', rateBasisPoints: 200 })
165
+ * ```
166
+ *
167
+ * @example Capped: 0.75% of transaction volume, max $50/event
168
+ * ```ts
169
+ * Pricing.percentOf({
170
+ * basis: 'transaction-volume',
171
+ * rateBasisPoints: 75,
172
+ * cap: { amount: 5000n, currency: 'USD' },
173
+ * })
174
+ * ```
175
+ */
176
+ percentOf(opts: {
177
+ basis: PercentOfBasis
178
+ rateBasisPoints: number
179
+ cap?: Money
180
+ floor?: Money
181
+ }): Pricing {
182
+ const result: Extract<Pricing, { kind: 'percent-of' }> = {
183
+ kind: 'percent-of',
184
+ basis: opts.basis,
185
+ rateBasisPoints: opts.rateBasisPoints,
186
+ }
187
+ if (opts.cap !== undefined) result.cap = opts.cap
188
+ if (opts.floor !== undefined) result.floor = opts.floor
189
+ return result
190
+ },
191
+ }
192
+
193
+ /** Convenience: build a Money value from a bigint + currency. */
194
+ export const money = (amount: bigint, currency: Currency = 'USD'): Money => ({
195
+ amount,
196
+ currency,
197
+ })
@@ -0,0 +1,106 @@
1
+ /**
2
+ * ProofPredicate — composable predicates that gate outcome-based settlement.
3
+ *
4
+ * Stripe's MPP Sessions ship escrow but no outcome-predicate-driven release —
5
+ * this module is the gap-filler. SaS's Service.outcomeContract.predicate
6
+ * uses these to express "definition of done."
7
+ *
8
+ * Seven leaf predicates + AND/OR composition:
9
+ * - schema-match : output matches schema
10
+ * - evaluator-pass : EvaluatorPanel approves at threshold
11
+ * - human-sign : human with signerRoles signs
12
+ * - external : external verifier (e.g. github CI + merged) approves
13
+ * - load-bearing-pass : a named subset of rubric items all pass (sb killThreshold)
14
+ * - overall-floor : N of total rubric items pass (sb killThreshold)
15
+ * - unmet-requirements-pass : no `severity: 'blocking'` UnmetRequirements remain
16
+ * (sb-n7d open-blocking gate); when `categories` is
17
+ * supplied, only those categories are checked.
18
+ */
19
+
20
+ export type ProofPredicate =
21
+ | { kind: 'schema-match'; schema: unknown }
22
+ | {
23
+ kind: 'evaluator-pass'
24
+ panelRef: string | 'self'
25
+ minScore: number | 'all-approved' | 'majority'
26
+ }
27
+ | { kind: 'human-sign'; signerRoles: string[]; when?: string }
28
+ | { kind: 'external'; verifier: string; spec: unknown }
29
+ | { kind: 'load-bearing-pass'; itemSet: string[] }
30
+ | { kind: 'overall-floor'; minPasses: number; outOfTotal: number }
31
+ | { kind: 'unmet-requirements-pass'; categories?: string[] }
32
+ | { kind: 'and'; predicates: ProofPredicate[] }
33
+ | { kind: 'or'; predicates: ProofPredicate[] }
34
+
35
+ export const SchemaMatch = (schema: unknown): ProofPredicate => ({
36
+ kind: 'schema-match',
37
+ schema,
38
+ })
39
+
40
+ export const EvaluatorPass = (opts: {
41
+ panelRef: string | 'self'
42
+ minScore: number | 'all-approved' | 'majority'
43
+ }): ProofPredicate => ({
44
+ kind: 'evaluator-pass',
45
+ panelRef: opts.panelRef,
46
+ minScore: opts.minScore,
47
+ })
48
+
49
+ export const HumanSign = (opts: { signerRoles: string[]; when?: string }): ProofPredicate => {
50
+ if (opts.when !== undefined) {
51
+ return { kind: 'human-sign', signerRoles: opts.signerRoles, when: opts.when }
52
+ }
53
+ return { kind: 'human-sign', signerRoles: opts.signerRoles }
54
+ }
55
+
56
+ export const External = (opts: { verifier: string; spec: unknown }): ProofPredicate => ({
57
+ kind: 'external',
58
+ verifier: opts.verifier,
59
+ spec: opts.spec,
60
+ })
61
+
62
+ export const LoadBearingPass = (itemSet: string[]): ProofPredicate => ({
63
+ kind: 'load-bearing-pass',
64
+ itemSet,
65
+ })
66
+
67
+ export const OverallFloor = (opts: { minPasses: number; outOfTotal: number }): ProofPredicate => ({
68
+ kind: 'overall-floor',
69
+ minPasses: opts.minPasses,
70
+ outOfTotal: opts.outOfTotal,
71
+ })
72
+
73
+ /**
74
+ * `UnmetRequirementsPass` — sb-n7d's open-blocking gate as a first-class
75
+ * predicate.
76
+ *
77
+ * Passes iff no `severity: 'blocking'` UnmetRequirement is present in the
78
+ * verify-time evaluation context. `severity: 'warning'` items are ignored.
79
+ *
80
+ * When `categories` is supplied, only requirements whose `category` matches
81
+ * one of the listed values are considered (others are ignored regardless of
82
+ * severity). When omitted, ALL categories are inspected.
83
+ *
84
+ * @example
85
+ * // Any blocking unmet requirement fails the predicate.
86
+ * UnmetRequirementsPass()
87
+ *
88
+ * // Only blocking items in the 'compliance' or 'security' buckets fail.
89
+ * UnmetRequirementsPass({ categories: ['compliance', 'security'] })
90
+ */
91
+ export const UnmetRequirementsPass = (opts?: { categories?: string[] }): ProofPredicate => {
92
+ if (opts?.categories !== undefined) {
93
+ return { kind: 'unmet-requirements-pass', categories: opts.categories }
94
+ }
95
+ return { kind: 'unmet-requirements-pass' }
96
+ }
97
+
98
+ export const AND = (...predicates: ProofPredicate[]): ProofPredicate => ({
99
+ kind: 'and',
100
+ predicates,
101
+ })
102
+
103
+ export const OR = (...predicates: ProofPredicate[]): ProofPredicate => ({
104
+ kind: 'or',
105
+ predicates,
106
+ })
@@ -0,0 +1,52 @@
1
+ /**
2
+ * RefundContract — typed refund machinery. The 7-pattern catalog (per startup-builder
3
+ * SERVICES.md) defines the contractual templates a Service can bind to.
4
+ *
5
+ * Stripe's Smart Disputes are consumer-chargeback-shaped; this is B2B SLA-shape.
6
+ */
7
+
8
+ export type RefundContractRef =
9
+ | 'no-charge-if-not-qualified'
10
+ | 'quality-floor-fail'
11
+ | 'sla-credit-on-late-delivery'
12
+ | 'sla-credit-on-late-close'
13
+ | 'partial-credit-on-partial-delivery'
14
+ | 'time-bounded-money-back'
15
+ | 'escalate-to-dispute'
16
+ | (string & { __brand?: 'RefundContractRef' })
17
+
18
+ /**
19
+ * Catalog of canonical refund contracts. Consumers reference by id; substrate
20
+ * resolves the contract semantics at settlement time.
21
+ */
22
+ export const RefundContracts = {
23
+ 'no-charge-if-not-qualified': {
24
+ description:
25
+ 'No charge unless EvaluatorPass + downstream verification confirms work delivered.',
26
+ triggersAt: 'pre-charge',
27
+ },
28
+ 'quality-floor-fail': {
29
+ description: 'Full refund when EvaluatorPanel rejects below quality floor.',
30
+ triggersAt: 'post-quality-review',
31
+ },
32
+ 'sla-credit-on-late-delivery': {
33
+ description: 'Credit equal to N% of invoice when delivery exceeds OutcomeContract.expiresAt.',
34
+ triggersAt: 'on-timeout',
35
+ },
36
+ 'sla-credit-on-late-close': {
37
+ description: 'Credit on monthly subscription when SLA target (e.g. close-by-day-5) breached.',
38
+ triggersAt: 'sla-breach',
39
+ },
40
+ 'partial-credit-on-partial-delivery': {
41
+ description: 'Pro-rata credit when fraction of work-units delivered (e.g. tickets resolved).',
42
+ triggersAt: 'post-delivery',
43
+ },
44
+ 'time-bounded-money-back': {
45
+ description: 'Full refund within N days of acceptance, no questions asked.',
46
+ triggersAt: 'on-customer-request',
47
+ },
48
+ 'escalate-to-dispute': {
49
+ description: 'Route to ESCALATED_TO_HUMAN_REVIEW state; manual resolution.',
50
+ triggersAt: 'on-customer-dispute',
51
+ },
52
+ } as const
@@ -0,0 +1,33 @@
1
+ /**
2
+ * SLAPolicy — service-level agreement with auto-credit / auto-refund / escalate
3
+ * on breach. The SaS book's "outcome-pricing requires you to stand behind quality"
4
+ * substrate.
5
+ */
6
+
7
+ export interface SLATarget {
8
+ metric:
9
+ | 'latency-ms'
10
+ | 'accuracy'
11
+ | 'on-time'
12
+ | 'completeness'
13
+ | 'first-contact-resolution'
14
+ | 'csat'
15
+ | string
16
+ /** Threshold value or expression (e.g. 'day-5', 0.95, 1000). */
17
+ threshold: number | string
18
+ }
19
+
20
+ export interface SLAPolicy {
21
+ $id: string
22
+ $type: 'SLAPolicy'
23
+ serviceRef: string
24
+ targets: SLATarget[]
25
+ onBreach: {
26
+ /** Percent of charge to credit back (0-100). */
27
+ creditPercent?: number
28
+ /** Percent of charge to refund (0-100). */
29
+ refundPercent?: number
30
+ /** Worker (Person/Agent/Role) to escalate to. */
31
+ escalateTo?: string
32
+ }
33
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Core value types — Money, Cost, Budget, SpendControl, CostModel.
3
+ *
4
+ * Money uses bigint in smallest currency unit (cents/satoshis/wei) for precision.
5
+ * Refs use plain string with brand comment; cross-package nominal types deferred.
6
+ */
7
+
8
+ export type FiatCurrency = 'USD' | 'EUR' | 'GBP' | 'JPY' | 'CAD' | 'AUD' | string
9
+ export type StablecoinCurrency = 'USDC' | 'PYUSD' | 'USDT' | 'USDG' | 'USDSui' | 'CASH' | string
10
+ export type CryptoCurrency = 'BTC' | 'ETH' | 'SOL'
11
+ export type Currency = FiatCurrency | StablecoinCurrency | CryptoCurrency
12
+
13
+ export interface Money {
14
+ amount: bigint
15
+ currency: Currency
16
+ }
17
+
18
+ /** Cost incurred by an Action — every cascade Function call captures one. */
19
+ export interface Cost {
20
+ $id: string
21
+ $type: 'Cost'
22
+ /** Reference to the underlying Action (digital-objects ActionRef shape). */
23
+ actionRef: string
24
+ amount: Money
25
+ /** Provider that incurred the cost: 'openai' | 'anthropic' | 'stripe' | ... */
26
+ provider: string
27
+ category: 'inference' | 'compute' | 'storage' | 'api' | 'human' | 'rail-fee' | 'other'
28
+ /** ISO-8601 timestamp. */
29
+ capturedAt: string
30
+ }
31
+
32
+ /** Where a Budget applies. */
33
+ export type BudgetScope =
34
+ | { kind: 'worker'; ref: string }
35
+ | { kind: 'function'; ref: string }
36
+ | { kind: 'goal'; ref: string }
37
+ | { kind: 'experiment'; ref: string }
38
+ | { kind: 'tenant'; ref: string }
39
+
40
+ export interface Budget {
41
+ $id: string
42
+ $type: 'Budget'
43
+ scope: BudgetScope
44
+ cap: Money
45
+ period: 'daily' | 'weekly' | 'monthly' | 'one-time'
46
+ /** ISO-8601 timestamp; absent for one-time budgets. */
47
+ resetAt?: string
48
+ }
49
+
50
+ export interface SpendControl {
51
+ budgetRef: string
52
+ /** 0-1 fraction of cap — warn when soft threshold crossed. */
53
+ soft?: number
54
+ /** 0-1 fraction of cap — block/escalate when hard threshold crossed. */
55
+ hard: number
56
+ onBreach: 'block' | 'escalate' | 'warn'
57
+ /** Worker to escalate to (Person/Agent/Role); ThingRef shape. */
58
+ escalateTo?: string
59
+ }
60
+
61
+ /** Declared cost model on a Function or Service. */
62
+ export interface CostModel {
63
+ /** Per-invocation flat cost (cents in smallest unit). */
64
+ perInvocation?: bigint
65
+ /** Per-transaction cost (e.g. per-token, per-row). */
66
+ perTx?: bigint
67
+ /** Per round of agent execution (e.g. per-dev-agent-round). */
68
+ perAgentRound?: bigint
69
+ /** Per external API call. */
70
+ perApiCall?: bigint
71
+ /** Per-symbol or per-unit-of-output. */
72
+ perUnit?: bigint
73
+ /** Hourly rate for human work. */
74
+ perHour?: bigint
75
+ }
package/src/goals.ts CHANGED
@@ -1,8 +1,73 @@
1
1
  /**
2
2
  * Business goals definition and tracking
3
+ *
4
+ * Uses org.ai Goal types for standardized goal definitions across the ecosystem.
3
5
  */
4
6
 
5
7
  import type { GoalDefinition } from './types.js'
8
+ import type {
9
+ Goal as OrgGoal,
10
+ Goals as OrgGoals,
11
+ GoalStatus,
12
+ GoalCategory,
13
+ GoalPriority,
14
+ } from 'org.ai'
15
+
16
+ // Re-export org.ai goal types for convenience
17
+ export type { OrgGoal, OrgGoals, GoalStatus, GoalCategory, GoalPriority }
18
+
19
+ /**
20
+ * Convert a business-as-code GoalDefinition to an org.ai Goal
21
+ *
22
+ * @param definition - Business goal definition
23
+ * @param id - Unique identifier for the goal
24
+ * @returns org.ai Goal object
25
+ */
26
+ export function toOrgGoal(definition: GoalDefinition, id: string): OrgGoal {
27
+ const result: OrgGoal = {
28
+ id,
29
+ name: definition.name,
30
+ description: definition.description || definition.name,
31
+ target: 100,
32
+ progress: definition.progress || 0,
33
+ status: definition.status || 'not-started',
34
+ }
35
+ if (definition.targetDate) {
36
+ result.targetDate = definition.targetDate
37
+ result.deadline = definition.targetDate
38
+ }
39
+ if (definition.owner) result.owner = definition.owner
40
+ if (definition.category) result.category = definition.category
41
+ if (definition.metrics) result.metrics = definition.metrics
42
+ if (definition.dependencies) result.dependencies = definition.dependencies
43
+ if (definition.metadata) result.metadata = definition.metadata
44
+ return result
45
+ }
46
+
47
+ /**
48
+ * Convert an org.ai Goal to a business-as-code GoalDefinition
49
+ *
50
+ * @param goal - org.ai Goal object
51
+ * @returns Business goal definition
52
+ */
53
+ export function fromOrgGoal(goal: OrgGoal): GoalDefinition {
54
+ const result: GoalDefinition = {
55
+ name: goal.name,
56
+ progress: typeof goal.progress === 'number' ? goal.progress : 0,
57
+ }
58
+ if (goal.description) result.description = goal.description
59
+ const cat = goal.category
60
+ if (cat) result.category = cat as NonNullable<GoalDefinition['category']>
61
+ if (goal.targetDate) result.targetDate = goal.targetDate
62
+ else if (goal.deadline) result.targetDate = goal.deadline
63
+ if (goal.owner) result.owner = goal.owner
64
+ if (goal.metrics) result.metrics = goal.metrics
65
+ const st = goal.status
66
+ if (st) result.status = st as NonNullable<GoalDefinition['status']>
67
+ if (goal.dependencies) result.dependencies = goal.dependencies
68
+ if (goal.metadata) result.metadata = goal.metadata
69
+ return result
70
+ }
6
71
 
7
72
  /**
8
73
  * Define a business goal with metrics and tracking
@@ -35,7 +100,7 @@ import type { GoalDefinition } from './types.js'
35
100
  * ```
36
101
  */
37
102
  export function Goals(definitions: GoalDefinition[]): GoalDefinition[] {
38
- return definitions.map(goal => validateAndNormalizeGoal(goal))
103
+ return definitions.map((goal) => validateAndNormalizeGoal(goal))
39
104
  }
40
105
 
41
106
  /**
@@ -73,20 +138,21 @@ export function updateProgress(goal: GoalDefinition, progress: number): GoalDefi
73
138
  }
74
139
 
75
140
  // Auto-update status based on progress
76
- let status = goal.status
141
+ let status: GoalDefinition['status']
77
142
  if (progress === 0) {
78
143
  status = 'not-started'
79
144
  } else if (progress === 100) {
80
145
  status = 'completed'
81
- } else if (progress > 0) {
146
+ } else {
82
147
  status = 'in-progress'
83
148
  }
84
149
 
85
- return {
150
+ const result: GoalDefinition = {
86
151
  ...goal,
87
152
  progress,
88
- status,
89
153
  }
154
+ if (status !== undefined) result.status = status
155
+ return result
90
156
  }
91
157
 
92
158
  /**
@@ -125,7 +191,7 @@ export function getGoalsByCategory(
125
191
  goals: GoalDefinition[],
126
192
  category: GoalDefinition['category']
127
193
  ): GoalDefinition[] {
128
- return goals.filter(g => g.category === category)
194
+ return goals.filter((g) => g.category === category)
129
195
  }
130
196
 
131
197
  /**
@@ -135,14 +201,14 @@ export function getGoalsByStatus(
135
201
  goals: GoalDefinition[],
136
202
  status: GoalDefinition['status']
137
203
  ): GoalDefinition[] {
138
- return goals.filter(g => g.status === status)
204
+ return goals.filter((g) => g.status === status)
139
205
  }
140
206
 
141
207
  /**
142
208
  * Get goals by owner
143
209
  */
144
210
  export function getGoalsByOwner(goals: GoalDefinition[], owner: string): GoalDefinition[] {
145
- return goals.filter(g => g.owner === owner)
211
+ return goals.filter((g) => g.owner === owner)
146
212
  }
147
213
 
148
214
  /**
@@ -159,7 +225,7 @@ export function calculateOverallProgress(goals: GoalDefinition[]): number {
159
225
  * Check for circular dependencies
160
226
  */
161
227
  export function hasCircularDependencies(goals: GoalDefinition[]): boolean {
162
- const goalMap = new Map(goals.map(g => [g.name, g]))
228
+ const goalMap = new Map(goals.map((g) => [g.name, g]))
163
229
 
164
230
  function checkCircular(goalName: string, visited = new Set<string>()): boolean {
165
231
  if (visited.has(goalName)) return true
@@ -178,14 +244,14 @@ export function hasCircularDependencies(goals: GoalDefinition[]): boolean {
178
244
  return false
179
245
  }
180
246
 
181
- return goals.some(goal => checkCircular(goal.name))
247
+ return goals.some((goal) => checkCircular(goal.name))
182
248
  }
183
249
 
184
250
  /**
185
251
  * Get goals in dependency order
186
252
  */
187
253
  export function sortByDependencies(goals: GoalDefinition[]): GoalDefinition[] {
188
- const goalMap = new Map(goals.map(g => [g.name, g]))
254
+ const goalMap = new Map(goals.map((g) => [g.name, g]))
189
255
  const sorted: GoalDefinition[] = []
190
256
  const visited = new Set<string>()
191
257
 
@@ -230,7 +296,7 @@ export function validateGoals(goals: GoalDefinition[]): { valid: boolean; errors
230
296
  }
231
297
 
232
298
  if (goal.dependencies) {
233
- const goalNames = new Set(goals.map(g => g.name))
299
+ const goalNames = new Set(goals.map((g) => g.name))
234
300
  for (const dep of goal.dependencies) {
235
301
  if (!goalNames.has(dep)) {
236
302
  errors.push(`Goal ${goal.name} depends on unknown goal: ${dep}`)