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.
- package/.turbo/turbo-build.log +4 -5
- package/CHANGELOG.md +53 -0
- package/README.md +2 -0
- package/dist/dollar.d.ts.map +1 -1
- package/dist/dollar.js +2 -2
- package/dist/dollar.js.map +1 -1
- package/dist/entities/organization.d.ts +4 -0
- package/dist/entities/organization.d.ts.map +1 -1
- package/dist/entities/organization.js +27 -18
- package/dist/entities/organization.js.map +1 -1
- package/dist/entities/planning.d.ts +87 -0
- package/dist/finance/account.d.ts +44 -0
- package/dist/finance/account.d.ts.map +1 -0
- package/dist/finance/account.js +6 -0
- package/dist/finance/account.js.map +1 -0
- package/dist/finance/authority.d.ts +78 -0
- package/dist/finance/authority.d.ts.map +1 -0
- package/dist/finance/authority.js +27 -0
- package/dist/finance/authority.js.map +1 -0
- package/dist/finance/card.d.ts +36 -0
- package/dist/finance/card.d.ts.map +1 -0
- package/dist/finance/card.js +6 -0
- package/dist/finance/card.js.map +1 -0
- package/dist/finance/identity.d.ts +30 -0
- package/dist/finance/identity.d.ts.map +1 -0
- package/dist/finance/identity.js +8 -0
- package/dist/finance/identity.js.map +1 -0
- package/dist/finance/index.d.ts +36 -0
- package/dist/finance/index.d.ts.map +1 -0
- package/dist/finance/index.js +22 -0
- package/dist/finance/index.js.map +1 -0
- package/dist/finance/ledger.d.ts +24 -0
- package/dist/finance/ledger.d.ts.map +1 -0
- package/dist/finance/ledger.js +8 -0
- package/dist/finance/ledger.js.map +1 -0
- package/dist/finance/merchant.d.ts +129 -0
- package/dist/finance/merchant.d.ts.map +1 -0
- package/dist/finance/merchant.js +21 -0
- package/dist/finance/merchant.js.map +1 -0
- package/dist/finance/outcome-contract.d.ts +139 -0
- package/dist/finance/outcome-contract.d.ts.map +1 -0
- package/dist/finance/outcome-contract.js +27 -0
- package/dist/finance/outcome-contract.js.map +1 -0
- package/dist/finance/port.d.ts +121 -0
- package/dist/finance/port.d.ts.map +1 -0
- package/dist/finance/port.js +10 -0
- package/dist/finance/port.js.map +1 -0
- package/dist/finance/pricing.d.ts +154 -0
- package/dist/finance/pricing.d.ts.map +1 -0
- package/dist/finance/pricing.js +79 -0
- package/dist/finance/pricing.js.map +1 -0
- package/dist/finance/proof-predicate.d.ts +92 -0
- package/dist/finance/proof-predicate.d.ts.map +1 -0
- package/dist/finance/proof-predicate.js +80 -0
- package/dist/finance/proof-predicate.js.map +1 -0
- package/dist/finance/refund.d.ts +44 -0
- package/dist/finance/refund.d.ts.map +1 -0
- package/dist/finance/refund.js +41 -0
- package/dist/finance/refund.js.map +1 -0
- package/dist/finance/sla.d.ts +25 -0
- package/dist/finance/sla.d.ts.map +1 -0
- package/dist/finance/sla.js +7 -0
- package/dist/finance/sla.js.map +1 -0
- package/dist/finance/types.d.ts +79 -0
- package/dist/finance/types.d.ts.map +1 -0
- package/dist/finance/types.js +8 -0
- package/dist/{canvas → finance}/types.js.map +1 -1
- package/dist/goals.d.ts +19 -0
- package/dist/goals.d.ts.map +1 -1
- package/dist/goals.js +81 -12
- package/dist/goals.js.map +1 -1
- package/dist/index.d.ts +12 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -7
- package/dist/index.js.map +1 -1
- package/dist/kpis.d.ts +19 -0
- package/dist/kpis.d.ts.map +1 -1
- package/dist/kpis.js +71 -6
- package/dist/kpis.js.map +1 -1
- package/dist/metrics.d.ts.map +1 -1
- package/dist/metrics.js +29 -24
- package/dist/metrics.js.map +1 -1
- package/dist/okrs.d.ts +34 -0
- package/dist/okrs.d.ts.map +1 -1
- package/dist/okrs.js +135 -13
- package/dist/okrs.js.map +1 -1
- package/dist/organization.d.ts.map +1 -1
- package/dist/organization.js +11 -11
- package/dist/organization.js.map +1 -1
- package/dist/process.d.ts.map +1 -1
- package/dist/process.js +13 -12
- package/dist/process.js.map +1 -1
- package/dist/product.d.ts.map +1 -1
- package/dist/product.js +9 -9
- package/dist/product.js.map +1 -1
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +194 -32
- package/dist/queries.js.map +1 -1
- package/dist/roles.d.ts +25 -31
- package/dist/roles.d.ts.map +1 -1
- package/dist/roles.js +37 -10
- package/dist/roles.js.map +1 -1
- package/dist/workflow.d.ts.map +1 -1
- package/dist/workflow.js +13 -12
- package/dist/workflow.js.map +1 -1
- package/package.json +20 -13
- package/src/dollar.ts +5 -2
- package/src/entities/organization.ts +31 -18
- package/src/finance/account.ts +48 -0
- package/src/finance/authority.ts +42 -0
- package/src/finance/card.ts +38 -0
- package/src/finance/identity.ts +31 -0
- package/src/finance/index.ts +117 -0
- package/src/finance/ledger.ts +26 -0
- package/src/finance/merchant.ts +127 -0
- package/src/finance/outcome-contract.ts +157 -0
- package/src/finance/port.ts +144 -0
- package/src/finance/pricing.ts +197 -0
- package/src/finance/proof-predicate.ts +106 -0
- package/src/finance/refund.ts +52 -0
- package/src/finance/sla.ts +33 -0
- package/src/finance/types.ts +75 -0
- package/src/goals.ts +78 -12
- package/src/index.ts +48 -18
- package/src/kpis.ts +62 -8
- package/src/metrics.ts +92 -79
- package/src/okrs.ts +120 -20
- package/src/organization.ts +12 -15
- package/src/process.ts +11 -12
- package/src/product.ts +8 -9
- package/src/queries.ts +238 -75
- package/src/roles.ts +62 -61
- package/src/workflow.ts +22 -15
- package/test/business.test.ts +282 -0
- package/test/dollar.test.ts +270 -0
- package/test/entities.test.ts +628 -0
- package/test/financials.test.ts +539 -0
- package/test/goals.test.ts +451 -0
- package/{src → test}/index.test.ts +1 -1
- package/test/kpis.test.ts +440 -0
- package/test/metrics.test.ts +744 -0
- package/test/okrs.test.ts +741 -0
- package/test/organization.test.ts +548 -0
- package/test/process.test.ts +503 -0
- package/test/product.test.ts +430 -0
- package/test/queries.test.ts +556 -0
- package/test/roles.test.ts +546 -0
- package/test/service.test.ts +450 -0
- package/test/types.test.ts +1141 -0
- package/test/vision.test.ts +214 -0
- package/test/workflow.test.ts +501 -0
- package/vitest.config.ts +47 -0
- package/LICENSE +0 -21
- package/dist/canvas/activities.d.ts +0 -19
- package/dist/canvas/activities.d.ts.map +0 -1
- package/dist/canvas/activities.js +0 -20
- package/dist/canvas/activities.js.map +0 -1
- package/dist/canvas/channels.d.ts +0 -20
- package/dist/canvas/channels.d.ts.map +0 -1
- package/dist/canvas/channels.js +0 -21
- package/dist/canvas/channels.js.map +0 -1
- package/dist/canvas/relationships.d.ts +0 -20
- package/dist/canvas/relationships.d.ts.map +0 -1
- package/dist/canvas/relationships.js +0 -21
- package/dist/canvas/relationships.js.map +0 -1
- package/dist/canvas/resources.d.ts +0 -20
- package/dist/canvas/resources.d.ts.map +0 -1
- package/dist/canvas/resources.js +0 -30
- package/dist/canvas/resources.js.map +0 -1
- package/dist/canvas/revenue.d.ts +0 -22
- package/dist/canvas/revenue.d.ts.map +0 -1
- package/dist/canvas/revenue.js +0 -30
- package/dist/canvas/revenue.js.map +0 -1
- package/dist/canvas/segments.d.ts +0 -20
- package/dist/canvas/segments.d.ts.map +0 -1
- package/dist/canvas/segments.js +0 -28
- package/dist/canvas/segments.js.map +0 -1
- package/dist/canvas/types.d.ts +0 -232
- package/dist/canvas/types.d.ts.map +0 -1
- package/dist/canvas/types.js +0 -8
- package/dist/canvas/value.d.ts +0 -20
- package/dist/canvas/value.d.ts.map +0 -1
- package/dist/canvas/value.js +0 -21
- package/dist/canvas/value.js.map +0 -1
- package/src/business.js +0 -108
- package/src/canvas/activities.ts +0 -32
- package/src/canvas/canvas.ts +0 -482
- package/src/canvas/channels.ts +0 -34
- package/src/canvas/costs.ts +0 -43
- package/src/canvas/economics.ts +0 -99
- package/src/canvas/index.ts +0 -206
- package/src/canvas/partnerships.ts +0 -34
- package/src/canvas/projections.ts +0 -141
- package/src/canvas/relationships.ts +0 -34
- package/src/canvas/resources.ts +0 -43
- package/src/canvas/revenue.ts +0 -56
- package/src/canvas/segments.ts +0 -42
- package/src/canvas/types.ts +0 -363
- package/src/canvas/value.ts +0 -34
- package/src/dollar.js +0 -106
- package/src/entities/assets.js +0 -322
- package/src/entities/business.js +0 -369
- package/src/entities/communication.js +0 -254
- package/src/entities/customers.js +0 -988
- package/src/entities/financials.js +0 -931
- package/src/entities/goals.js +0 -799
- package/src/entities/index.js +0 -197
- package/src/entities/legal.js +0 -300
- package/src/entities/market.js +0 -300
- package/src/entities/marketing.js +0 -1156
- package/src/entities/offerings.js +0 -726
- package/src/entities/operations.js +0 -786
- package/src/entities/organization.js +0 -806
- package/src/entities/partnerships.js +0 -299
- package/src/entities/planning.js +0 -270
- package/src/entities/projects.js +0 -348
- package/src/entities/risk.js +0 -292
- package/src/entities/sales.js +0 -1247
- package/src/financials.js +0 -296
- package/src/goals.js +0 -214
- package/src/index.js +0 -131
- package/src/index.test.js +0 -274
- package/src/kpis.js +0 -231
- package/src/metrics.js +0 -324
- package/src/okrs.js +0 -268
- package/src/organization.js +0 -172
- package/src/process.js +0 -240
- package/src/product.js +0 -144
- package/src/queries.js +0 -414
- package/src/roles.js +0 -254
- package/src/service.js +0 -139
- package/src/types.js +0 -4
- package/src/vision.js +0 -67
- package/src/workflow.js +0 -246
- 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
|
|
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
|
|
146
|
+
} else {
|
|
82
147
|
status = 'in-progress'
|
|
83
148
|
}
|
|
84
149
|
|
|
85
|
-
|
|
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}`)
|