@happyvertical/smrt-support 0.40.69 → 0.41.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.
@@ -0,0 +1,26 @@
1
+ import { DatabaseInterface, MinorUnitsPreflightResult, MinorUnitsRescaleResult, MoneyColumnTarget } from '@happyvertical/smrt-core/migrations';
2
+ /** Marker recorded in `_smrt_backfills` once the rescale has run. */
3
+ export declare const SUPPORT_MONEY_MINOR_UNITS_BACKFILL = "@happyvertical/smrt-support:money-minor-units:v1";
4
+ /** Every support money column that held major units before #2401. */
5
+ export declare const SUPPORT_MONEY_COLUMNS: MoneyColumnTarget[];
6
+ /**
7
+ * Report what {@link migrateSupportMoneyToMinorUnits} would do, without writing
8
+ * anything.
9
+ */
10
+ export declare function preflightSupportMoneyMinorUnits(db: DatabaseInterface, options?: {
11
+ scale?: number;
12
+ engineHint?: string;
13
+ }): Promise<MinorUnitsPreflightResult>;
14
+ /**
15
+ * Convert every support money column from floating-point major units to integer
16
+ * minor units. Idempotent via a `_smrt_backfills` marker.
17
+ *
18
+ * @throws `MinorUnitsPreflightError` when a row would be rounded or would
19
+ * overflow `int4` and `force` was not set.
20
+ */
21
+ export declare function migrateSupportMoneyToMinorUnits(db: DatabaseInterface, options?: {
22
+ scale?: number;
23
+ engineHint?: string;
24
+ force?: boolean;
25
+ }): Promise<MinorUnitsRescaleResult>;
26
+ //# sourceMappingURL=moneyMinorUnits.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"moneyMinorUnits.d.ts","sourceRoot":"","sources":["../../src/migrations/moneyMinorUnits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,iBAAiB,EAGvB,MAAM,qCAAqC,CAAC;AAE7C,qEAAqE;AACrE,eAAO,MAAM,kCAAkC,qDACK,CAAC;AAErD,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,EAAE,iBAAiB,EAIpD,CAAC;AAEF;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,iBAAiB,EACrB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,yBAAyB,CAAC,CAEpC;AAED;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAC7C,EAAE,EAAE,iBAAiB,EACrB,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,GACrE,OAAO,CAAC,uBAAuB,CAAC,CAMlC"}
@@ -5,7 +5,12 @@ export declare class SupportCompensationPlan extends SmrtObject {
5
5
  specialistId: string | null;
6
6
  /** Display name (inherited `name`; uniqueness comes from conflictColumns). */
7
7
  name: string;
8
- /** Hourly earning rate for delivered support time. */
8
+ /**
9
+ * Earning rate for delivered support time, in **minor units per hour**, so
10
+ * `hours * hourlyRate` yields integer minor units once rounded. Stays
11
+ * DECIMAL: a rate is inherently fractional and INTEGER would truncate
12
+ * sub-unit rates (#2401).
13
+ */
9
14
  hourlyRate: number;
10
15
  currency: string;
11
16
  /** Effective-dating: null bounds mean open-ended. */
@@ -1 +1 @@
1
- {"version":3,"file":"support-compensation-plan.d.ts","sourceRoot":"","sources":["../../src/models/support-compensation-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAGL,cAAc,EACd,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAOlC,qBAQa,uBAAwB,SAAQ,UAAU;IAErD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,oEAAoE;IAEpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAM;IAElB,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAO;IAGzB,QAAQ,EAAE,MAAM,CAAS;IAEzB,qDAAqD;IACrD,aAAa,EAAE,IAAI,GAAG,IAAI,CAAQ;IAElC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEhC,6BAA6B;IAE7B,MAAM,EAAE,MAAM,CAAY;IAE1B,wEAAwE;IAExE,KAAK,EAAE,MAAM,CAAQ;IAErB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAInC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI9C,0DAA0D;IAC1D,aAAa,CAAC,EAAE,GAAE,IAAiB,GAAG,OAAO;CAM9C;AAED,qBAAa,iCAAkC,SAAQ,cAAc,CAAC,uBAAuB,CAAC;IAC5F,MAAM,CAAC,QAAQ,CAAC,UAAU,iCAA2B;IAErD;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,YAAY,EAAE,MAAM,EACpB,EAAE,GAAE,IAAiB,EACrB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAO,GACzC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;CA6B3C;AAED,eAAe,uBAAuB,CAAC"}
1
+ {"version":3,"file":"support-compensation-plan.d.ts","sourceRoot":"","sources":["../../src/models/support-compensation-plan.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAGL,cAAc,EACd,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAOlC,qBAQa,uBAAwB,SAAQ,UAAU;IAErD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE/B,oEAAoE;IAEpE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAM;IAElB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAO;IAGzB,QAAQ,EAAE,MAAM,CAAS;IAEzB,qDAAqD;IACrD,aAAa,EAAE,IAAI,GAAG,IAAI,CAAQ;IAElC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAEhC,6BAA6B;IAE7B,MAAM,EAAE,MAAM,CAAY;IAE1B,wEAAwE;IAExE,KAAK,EAAE,MAAM,CAAQ;IAErB,QAAQ,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAInC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAI9C,0DAA0D;IAC1D,aAAa,CAAC,EAAE,GAAE,IAAiB,GAAG,OAAO;CAM9C;AAED,qBAAa,iCAAkC,SAAQ,cAAc,CAAC,uBAAuB,CAAC;IAC5F,MAAM,CAAC,QAAQ,CAAC,UAAU,iCAA2B;IAErD;;;;;;;;;;;OAWG;IACG,oBAAoB,CACxB,YAAY,EAAE,MAAM,EACpB,EAAE,GAAE,IAAiB,EACrB,OAAO,GAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAO,GACzC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC;CA6B3C;AAED,eAAe,uBAAuB,CAAC"}
@@ -41,14 +41,28 @@ export declare class SupportPlan extends SmrtObject {
41
41
  pauseStatuses: string;
42
42
  /** Escalation policy: JSON array of {@link EscalationStep}, level order. */
43
43
  escalationPolicy: string;
44
- /** Recurring availability/retainer fee (0.0 = pure hourly support). */
44
+ /**
45
+ * Recurring availability/retainer fee, in **integer minor units** of
46
+ * {@link currency} — `$19.99` is `1999`; `0` means pure hourly support.
47
+ *
48
+ * The `= 0` initializer is load-bearing: SMRT maps an integer literal to
49
+ * INTEGER and a decimal literal to DECIMAL, and money is exact (#2401).
50
+ */
45
51
  availabilityFeeAmount: number;
46
52
  currency: string;
47
53
  /** Included support minutes per period (0 = none included). */
48
54
  includedMinutes: number;
49
- /** Metered hourly rate for time beyond the included minutes. */
55
+ /**
56
+ * Metered rate for time beyond the included minutes, in **minor units per
57
+ * hour**, so `hours * rate` yields minor units. Stays DECIMAL: a rate is
58
+ * inherently fractional and INTEGER would truncate sub-unit rates (#2401).
59
+ */
50
60
  overageHourlyRate: number;
51
- /** Premium hourly rate for on-call work (0.0 = use overage rate). */
61
+ /**
62
+ * Premium rate for on-call work, in minor units per hour (`0.0` = use the
63
+ * overage rate). Stays DECIMAL for the same reason as
64
+ * {@link overageHourlyRate} (#2401).
65
+ */
52
66
  onCallHourlyRate: number;
53
67
  /**
54
68
  * Time-entry approval policy for work under this plan (FR-36): JSON
@@ -1 +1 @@
1
- {"version":3,"file":"support-plan.d.ts","sourceRoot":"","sources":["../../src/models/support-plan.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"support-plan.d.ts","sourceRoot":"","sources":["../../src/models/support-plan.ts"],"names":[],"mappings":"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"}
@@ -6,7 +6,12 @@ export declare class SupportCharge extends SmrtObject {
6
6
  caseId: string | null;
7
7
  /** Plan the pricing derived from (null = no plan; zero-amount charge). */
8
8
  planId: string | null;
9
- /** Client charge amount for the billable time. */
9
+ /**
10
+ * Client charge for the billable time, in **integer minor units** of
11
+ * {@link currency} (#2401). Derived by the approval service, which rounds
12
+ * `(hours * rate)` to a whole minor unit at the one boundary where a rate
13
+ * meets money, so every comparison downstream is exact.
14
+ */
10
15
  amount: number;
11
16
  currency: string;
12
17
  /** Seconds priced at the metered rate (after included-time consumption). */
@@ -36,7 +41,11 @@ export declare class SupportCompensation extends SmrtObject {
36
41
  specialistId: string | null;
37
42
  /** Compensation plan the earning derived from (null = no plan resolved). */
38
43
  compensationPlanId: string | null;
39
- /** Provider earning amount for the payable time. */
44
+ /**
45
+ * Provider earning for the payable time, in **integer minor units** of
46
+ * {@link currency} (#2401). Converts with {@link SupportCharge.amount}: the
47
+ * delivery margin is the difference between the two.
48
+ */
40
49
  amount: number;
41
50
  currency: string;
42
51
  /** Seconds compensated. */
@@ -1 +1 @@
1
- {"version":3,"file":"support-settlement.d.ts","sourceRoot":"","sources":["../../src/models/support-settlement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAGL,cAAc,EACd,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAkB,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3E,qBAQa,aAAc,SAAQ,UAAU;IAE3C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,WAAW,EAAE,MAAM,CAAM;IAGzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE7B,0EAA0E;IAE1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE7B,kDAAkD;IAClD,MAAM,EAAE,MAAM,CAAO;IAGrB,QAAQ,EAAE,MAAM,CAAS;IAEzB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAK;IAE5B,4DAA4D;IAC5D,sBAAsB,EAAE,MAAM,CAAK;IAEnC;;;;OAIG;IAEH,YAAY,EAAE,MAAM,CAAQ;IAG5B,MAAM,EAAE,uBAAuB,CAAa;IAE5C,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAGhC,QAAQ,EAAE,MAAM,CAAQ;IAExB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI1C,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGtD;AAED,qBAAa,uBAAwB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACxE,MAAM,CAAC,QAAQ,CAAC,UAAU,uBAAiB;IAErC,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAKhE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CAGxD;AAED,qBAQa,mBAAoB,SAAQ,UAAU;IAEjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,WAAW,EAAE,MAAM,CAAM;IAGzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,4EAA4E;IAE5E,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEzC,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAO;IAGrB,QAAQ,EAAE,MAAM,CAAS;IAEzB,2BAA2B;IAC3B,cAAc,EAAE,MAAM,CAAK;IAE3B,kEAAkE;IAElE,YAAY,EAAE,MAAM,CAAQ;IAG5B,MAAM,EAAE,uBAAuB,CAAa;IAE5C,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAGhC,QAAQ,EAAE,MAAM,CAAQ;IAExB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI1C,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGtD;AAED,qBAAa,6BAA8B,SAAQ,cAAc,CAAC,mBAAmB,CAAC;IACpF,MAAM,CAAC,QAAQ,CAAC,UAAU,6BAAuB;IAE3C,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAKtE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;CAG1E;AAED,eAAe,aAAa,CAAC"}
1
+ {"version":3,"file":"support-settlement.d.ts","sourceRoot":"","sources":["../../src/models/support-settlement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAGL,cAAc,EACd,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAAkB,KAAK,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3E,qBAQa,aAAc,SAAQ,UAAU;IAE3C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,WAAW,EAAE,MAAM,CAAM;IAGzB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE7B,0EAA0E;IAE1E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAQ;IAE7B;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAK;IAGnB,QAAQ,EAAE,MAAM,CAAS;IAEzB,4EAA4E;IAC5E,eAAe,EAAE,MAAM,CAAK;IAE5B,4DAA4D;IAC5D,sBAAsB,EAAE,MAAM,CAAK;IAEnC;;;;OAIG;IAEH,YAAY,EAAE,MAAM,CAAQ;IAG5B,MAAM,EAAE,uBAAuB,CAAa;IAE5C,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAGhC,QAAQ,EAAE,MAAM,CAAQ;IAExB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI1C,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGtD;AAED,qBAAa,uBAAwB,SAAQ,cAAc,CAAC,aAAa,CAAC;IACxE,MAAM,CAAC,QAAQ,CAAC,UAAU,uBAAiB;IAErC,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC;IAKhE,OAAO,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;CAGxD;AAED,qBAQa,mBAAoB,SAAQ,UAAU;IAEjD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAQ;IAG/B,WAAW,EAAE,MAAM,CAAM;IAGzB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnC,4EAA4E;IAE5E,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEzC;;;;OAIG;IACH,MAAM,EAAE,MAAM,CAAK;IAGnB,QAAQ,EAAE,MAAM,CAAS;IAEzB,2BAA2B;IAC3B,cAAc,EAAE,MAAM,CAAK;IAE3B,kEAAkE;IAElE,YAAY,EAAE,MAAM,CAAQ;IAG5B,MAAM,EAAE,uBAAuB,CAAa;IAE5C,WAAW,EAAE,IAAI,GAAG,IAAI,CAAQ;IAGhC,QAAQ,EAAE,MAAM,CAAQ;IAExB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAI1C,eAAe,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;CAGtD;AAED,qBAAa,6BAA8B,SAAQ,cAAc,CAAC,mBAAmB,CAAC;IACpF,MAAM,CAAC,QAAQ,CAAC,UAAU,6BAAuB;IAE3C,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAKtE,aAAa,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC;CAG1E;AAED,eAAe,aAAa,CAAC"}
@@ -73,8 +73,10 @@ interface ChargeDerivation {
73
73
  billableSeconds: number;
74
74
  includedSecondsBefore: number;
75
75
  includedSecondsApplied: number;
76
+ /** Minor units per hour — see {@link SupportPlan.overageHourlyRate}. */
76
77
  hourlyRate: number;
77
78
  rateSource: 'overage' | 'on_call' | 'none';
79
+ /** Integer minor units (#2401). */
78
80
  amount: number;
79
81
  }
80
82
  /**
@@ -132,7 +134,9 @@ export declare class TimeEntryApprovalService {
132
134
  * {@link TimeEntryApprovalDeniedError} when the acting principal does not
133
135
  * satisfy it.
134
136
  */
135
- protected determinePath(entry: ServiceTimeEntry, policy: TimeApprovalPolicy, chargeAmount: number, principal: SupportPrincipal | undefined, supportCase: SupportCase | null): TimeEntryApprovalPath;
137
+ protected determinePath(entry: ServiceTimeEntry, policy: TimeApprovalPolicy,
138
+ /** Integer minor units, same unit as `policy.thresholdAmount` (#2401). */
139
+ chargeAmount: number, principal: SupportPrincipal | undefined, supportCase: SupportCase | null): TimeEntryApprovalPath;
136
140
  /**
137
141
  * Charge math from the Managed Support Plan terms: remaining included time
138
142
  * (per case — period windows are #1925's scope) absorbs first, the
@@ -1 +1 @@
1
- {"version":3,"file":"time-entry-approval-service.d.ts","sourceRoot":"","sources":["../../src/services/time-entry-approval-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"time-entry-approval-service.d.ts","sourceRoot":"","sources":["../../src/services/time-entry-approval-service.ts"],"names":[],"mappings":"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"}
@@ -3,13 +3,13 @@
3
3
  "sensitiveFieldsExcluded": true,
4
4
  "generatedAt": "1970-01-01T00:00:00.000Z",
5
5
  "packageName": "@happyvertical/smrt-support",
6
- "packageVersion": "0.40.69",
6
+ "packageVersion": "0.41.0",
7
7
  "sourceManifestPath": "dist/manifest.json",
8
8
  "agentDocPath": "AGENTS.md",
9
9
  "sourceHashes": {
10
- "manifest": "75d94ae5616e58ee704f1bcabea72f79a0891e00853f31d43d33f394649c074a",
11
- "packageJson": "1295f7f26d8ae55d1f09ce0497ad7d83f0cc890e0329b87077db4cce3c5db379",
12
- "agents": "3e834b4860801f2647b32bb1bc2169373a9715a946338895426461ff16b646d3"
10
+ "manifest": "00aa9b02ca4d32fe55fcf1c087848a74f205ae573d6260cdca630088090f8958",
11
+ "packageJson": "cff4df772b881af92feb3a250b40aa44f6c44ba5f2817a7091c7eba0610f430a",
12
+ "agents": "94c5793e435f84b7f1a8366e30288f27a480cf6c0a78e2ea61fef67733f1f7be"
13
13
  },
14
14
  "exports": [
15
15
  ".",
@@ -280,6 +280,11 @@
280
280
  "mode": "optional",
281
281
  "field": "tenantId"
282
282
  },
283
+ "conflictColumns": [
284
+ "tenant_id",
285
+ "slug",
286
+ "context"
287
+ ],
283
288
  "surfaces": [
284
289
  {
285
290
  "kind": "api",
@@ -553,6 +558,11 @@
553
558
  "mode": "optional",
554
559
  "field": "tenantId"
555
560
  },
561
+ "conflictColumns": [
562
+ "tenant_id",
563
+ "slug",
564
+ "context"
565
+ ],
556
566
  "surfaces": [
557
567
  {
558
568
  "kind": "api",
@@ -728,6 +738,11 @@
728
738
  "mode": "optional",
729
739
  "field": "tenantId"
730
740
  },
741
+ "conflictColumns": [
742
+ "tenant_id",
743
+ "slug",
744
+ "context"
745
+ ],
731
746
  "surfaces": [
732
747
  {
733
748
  "kind": "api",
@@ -1141,6 +1156,11 @@
1141
1156
  "mode": "optional",
1142
1157
  "field": "tenantId"
1143
1158
  },
1159
+ "conflictColumns": [
1160
+ "tenant_id",
1161
+ "slug",
1162
+ "context"
1163
+ ],
1144
1164
  "surfaces": [
1145
1165
  {
1146
1166
  "kind": "api",
@@ -1979,9 +1999,9 @@
1979
1999
  },
1980
2000
  {
1981
2001
  "name": "availabilityFeeAmount",
1982
- "type": "decimal",
2002
+ "type": "integer",
1983
2003
  "required": false,
1984
- "columnType": "REAL",
2004
+ "columnType": "INTEGER",
1985
2005
  "default": 0
1986
2006
  },
1987
2007
  {
@@ -2920,6 +2940,11 @@
2920
2940
  "mode": "optional",
2921
2941
  "field": "tenantId"
2922
2942
  },
2943
+ "conflictColumns": [
2944
+ "tenant_id",
2945
+ "slug",
2946
+ "context"
2947
+ ],
2923
2948
  "surfaces": [
2924
2949
  {
2925
2950
  "kind": "api",
@@ -3035,9 +3060,9 @@
3035
3060
  },
3036
3061
  {
3037
3062
  "name": "amount",
3038
- "type": "decimal",
3063
+ "type": "integer",
3039
3064
  "required": false,
3040
- "columnType": "REAL",
3065
+ "columnType": "INTEGER",
3041
3066
  "default": 0
3042
3067
  },
3043
3068
  {
@@ -3257,9 +3282,9 @@
3257
3282
  },
3258
3283
  {
3259
3284
  "name": "amount",
3260
- "type": "decimal",
3285
+ "type": "integer",
3261
3286
  "required": false,
3262
- "columnType": "REAL",
3287
+ "columnType": "INTEGER",
3263
3288
  "default": 0
3264
3289
  },
3265
3290
  {
@@ -3957,6 +3982,11 @@
3957
3982
  "mode": "optional",
3958
3983
  "field": "tenantId"
3959
3984
  },
3985
+ "conflictColumns": [
3986
+ "tenant_id",
3987
+ "slug",
3988
+ "context"
3989
+ ],
3960
3990
  "surfaces": [
3961
3991
  {
3962
3992
  "kind": "api",
@@ -5055,5 +5085,5 @@
5055
5085
  "polymorphicAssociations": 0,
5056
5086
  "uuidColumns": 92
5057
5087
  },
5058
- "agentDoc": "# @happyvertical/smrt-support\n\nAI-first Support Cases for the SMRT framework (epic #1934). Every inbound\nsupport interaction — chat message or email — creates or joins one canonical,\ntenant-scoped **Support Case**; channels stay transports, never separate\nsystems of record. The package owns the case lifecycle, channel intake, the\nAutomated Support Response workflow with lossless **Human Handoff**,\nProject-qualified routing, **Service Target** clocks with timed escalation,\nand auditable **Service Time Entries** whose client charges and provider\ncompensation stay separate. Since #1955 the canonical `ServiceTimeEntry` class\nis owned by `smrt-projects`; this package exposes a compatibility subtype that\nrestates the shared fields for isolated manifest scanning. Both imports use the\nunchanged `service_time_entries` table, so no data is duplicated.\n\n**Boundary (FR-29a):** Support owns tickets, interactions, routing, and\nservice targets. Delivery Operations owns repository work — a **Delivery\nHandoff** links a case to a Development Work Item (e.g.\n`@happyvertical/smrt-projects:Issue`) via a `SupportWorkLink` without ever\ndriving its execution; the delivery side reports status echoes back.\n\n## Models\n\nCase-side models are **internal**: generated API/CLI/MCP surfaces are\nread-only (`list`/`get`) and every write goes through the service facades\n(the `smrt-chat` `ChatService` idiom). Operational configuration models\n(bindings, policies, specialists, qualifications, availability) expose full\ngenerated CRUD; **commercial terms** (`SupportPlan`,\n`SupportCompensationPlan`) are read-only too — their writes flow through the\n`support.manage-plans`-gated `SupportPlanAdminService`.\n\n### Case core (#1926)\n\n- **SupportCase** (`support_cases`) — canonical ticket: `caseNumber`,\n `subject`, `status` (`new → triaged → assigned → in_progress →\n waiting_on_client|escalated → resolved → closed`, guarded at save time via\n the WeakMap + authoritative-DB-read idiom from S5 #1390), `priority`,\n `severity` (plan-defined key), `category`, `sensitive`, `channelKind`,\n `clientProfileId`/`openedByProfileId` (crossPackageRef → Profile),\n `projectId` (app-defined string — deliberately not an FK), `bindingId`,\n `threadKey` (create-or-join dedup key), `planId` + `planSnapshot` (terms\n frozen at apply time), assignment fields, response stamps\n (`acknowledgedAt`, `firstRespondedAt`), `humanRequestedAt`, resolution\n fields, `reopenCount`/`lastReopenedAt`, `aiEnabled`.\n- **SupportInteraction** (`support_interactions`,\n `conflictColumns: ['source_key']`) — channel-neutral transport record:\n `direction` (`inbound`/`outbound`/`internal`), `channelKind`, `actorKind`\n (`client`/`specialist`/`agent`/`system`), denormalized `body`, and the\n qualified `sourceType`/`sourceId` + globally unique `sourceKey`\n (`chat:<messageId>`, `email:<emailId>`, `manual:<uuid>`) that makes\n re-ingestion idempotent.\n- **SupportCaseEvent** (`support_case_events`) — append-only audit trail\n (transitions, assignments, AI runs, handoffs, escalations, target state,\n work links, reopens). Together with interactions this is the complete\n context a Human Handoff transfers.\n- **SupportChannelBinding** (`support_channel_bindings`,\n `conflictColumns: ['target_type','target_id']`) — marks a container as a\n support intake channel: `bindingKind` (`chat_room`/`email_account`),\n qualified `targetType` + `targetId`, default client/project/plan,\n `selfAddresses` (email direction heuristic). Unbound containers are never\n touched.\n- **SupportWorkLink** (`support_work_links`) — Support/Development Work Item\n links with a read-only `status` echo.\n\n### AI workflow (#1928)\n\n- **SupportPolicy** (`support_policies`) — what the AI may do without a\n human: `autoAcknowledge`/`autoClassify`/`autoAnswer` (on by default),\n `autoTroubleshoot`/`autoResolve` (off by default),\n `autoResolveMaxSeverity`, `confidenceThreshold`, `maxAutoAttempts`,\n `sensitiveCategories`, `allowedTools`, `autoSendEmailReplies` (off —\n email replies are drafted, not sent). Resolution is most-specific-wins on\n `(planId, projectId)`; no row → `DEFAULT_SUPPORT_POLICY` (conservative).\n- **SupportAiRun** (`support_ai_runs`) — append-only audit of each phase\n (`acknowledge`/`classify`/`answer`/`troubleshoot`/`resolve`) with\n `confidence`, classification output, knowledge refs, tool calls, outcome\n (`completed`/`skipped`/`failed`/`handed_off`), and `correlationId`.\n\n### Routing, targets, escalation (#1929)\n\n- **SupportSpecialist** (`support_specialists`) — specialist role record on a\n Profile (the commerce `Customer` role-record idiom): `languages`,\n `timezone`, `maxConcurrentCases`, `onCallPriority`, status.\n- **SupportQualification** (`support_qualifications`) — effective-dated\n Project Support Qualification (`trainee`/`qualified`/`expert`).\n- **SupportAvailability** (`support_availability_windows`) — weekly windows\n plus `on_call`/`time_off` spans.\n- **SupportPlan** (`support_plans`, `conflictColumns:\n ['tenant_id','plan_key']`) — the Managed Support Plan: coverage calendar +\n holidays + timezone, channels, severity definitions, per-severity\n acknowledgement/response/update/resolution target minutes, `pauseStatuses`\n (clocks pause only when the plan says so), escalation policy steps,\n availability fee, `includedMinutes`, `overageHourlyRate`,\n `onCallHourlyRate`, time-approval policy, optional\n `subscriptionPlanKey` binding (string, not a dependency).\n `snapshotTerms()` feeds `SupportCase.planSnapshot`.\n- **SupportServiceTarget** (`support_service_targets`,\n `conflictColumns: ['case_id','target_type','cycle']`) — one clock per\n target type (update clocks recur via `cycle`), with pause accounting and\n `escalationJobId` (the one-shot `_smrt_jobs` row scheduled at `dueAt`,\n cancelled on satisfy).\n- **SupportEscalation** (`support_escalations`) — the audit record of one\n escalation firing.\n- **SupportCompensationPlan** (`support_compensation_plans`) —\n effective-dated Support Specialist earning terms; specialist-specific plan\n wins over the tenant default at the work instant. Deliberately separate\n from `SupportPlan` (client pricing) so margin stays measurable.\n\n### Service time (#1930)\n\n- **ServiceTimeEntry** (`service_time_entries`) — auditable Professional\n Service duration: work-context-generic (`caseId` and/or polymorphic\n `workRefType`/`workRefId`), delivering Participant (`participantKind`\n `human`/`agent` + `participantProfileId`/`agentRef`), `source`\n (`timer`/`manual`/`import`/`agent` — one shared validation/audit\n contract), evidence JSON, and lifecycle `draft → submitted →\n approved|rejected`, `approved → corrected` only. **Approved entries are\n immutable**: the work-defining fields are frozen at save time (the\n `LicenseSale` WeakMap-freeze idiom, backed by an authoritative DB-row\n comparison); disputes create explicit corrections via `correctionOfId`.\n- **SupportCharge** / **SupportCompensation** (`support_charges` /\n `support_compensations`, one per time entry) — derived commercial\n snapshots: client amount from the Managed Support Plan (included-time\n consumption then metered overage) vs provider amount from the Support\n Compensation Plan, each with a frozen `rateSnapshot`. Margin = charge −\n compensation, computed by readers; settlement/invoicing composes at the\n app/accounting boundary (#1925) and is out of scope here.\n\n## Services\n\n- **SupportCaseService** — the closed write facade: `openCase`,\n `recordInteraction` (idempotent on `sourceKey`; stamps\n `acknowledgedAt`/`firstRespondedAt`; inbound un-parks\n `waiting_on_client`), `transition`, `assign`, `resolve`, `close`,\n `reopen` (preserves prior resolution in the reopen event), `requestHuman`,\n `linkWork`/`recordWorkStatus` (Delivery Handoff), `recordEvent`,\n `getTimeline` (merged interactions + events).\n- **SupportIntakeService** — create-or-join intake for both channels:\n `ingestChatMessage` (bound `ChatRoom`; `chat:<roomId>[:<threadId>]` thread\n keys; only `role: 'user'` messages), `ingestEmail` (bound `EmailAccount`;\n RFC-thread joins via `inReplyTo`; `selfAddresses` skip; unresolved senders\n park the case with `metadata.unresolvedClient`), plus\n `registerSupportIntake()` — an opt-in `GlobalInterceptors.afterSave`\n observer keyed on the `ChatMessage`/`Email` class names that never throws\n into the source package's save path. Join semantics: open case → join;\n resolved → reopen; closed → new case.\n- **SupportAiWorkflow** — the Automated Support Response (FR-28a):\n `processIntake`/`processCase` run acknowledge → classify → answer →\n resolve under the resolved `SupportPolicy` (conservative defaults), each\n phase writing a `SupportAiRun` + `ai_run` case event. AI calls go through\n the injected `SupportAiBoundary` (default uses `supportCase.do()` with\n defensive JSON parsing that fails toward the human); knowledge comes from\n the pluggable `SupportKnowledgeProvider` (default no-op). Classification\n never overwrites human triage; email answers are drafted\n (`metadata.draft`) unless the policy allows sending. Handoff triggers —\n `client_request`, `low_confidence`, `high_severity`, `sensitive`,\n `failed_resolution`, `policy` (attempt budget) — are always active.\n- **HumanHandoffService** — the lossless Human Handoff (FR-28b):\n `handoff` assembles the full context package (case state + compact\n timeline + AI runs) into the `handoff` event, stamps\n `metadata.activeHandoff` for the no-repeat guarantee (repeat triggers are\n deduped events), routes through the injected `assignSpecialist` seam\n (#1929's routing service plugs in; default queues `new` cases to\n `triaged`), and `releaseHandoff` clears the flag.\n- **ServiceTargetEngine** — Service Target clocks (FR-30/31):\n `startTargetsForCase` derives per-severity clocks from the case's\n `planSnapshot` (live plan fallback) with due times computed in covered\n time (`coverage-calendar.ts`: timezone-aware weekly windows + holidays;\n empty coverage = 24×7) and schedules one-shot `_smrt_jobs` escalation jobs\n at `dueAt`; `onInteractionRecorded` satisfies acknowledgement/response and\n recurs `update` cycles; `onCaseTransition` pauses/resumes clocks only when\n the plan's `pauseStatuses` say so (recomputing remaining covered minutes)\n and settles targets on resolve. Breach → `SupportServiceTarget.\n checkAndEscalate` (background-eligible, at-least-once-safe) marks the\n breach and escalates through the plan's policy steps (notify/reassign,\n delayed follow-up levels), transitioning the case to `escalated`.\n- **SupportPlanAdminService** — the permission-gated write surface for\n commercial terms: create/update/archive for Managed Support Plans and\n Support Compensation Plans, every act behind `support.manage-plans` with\n cross-tenant writes refused (generated CRUD on both models is read-only).\n- **SupportRoutingService** — explainable ranked routing (FR-30):\n `rankSpecialists` hard-filters on active status, effective Project\n Support Qualification, workload cap, and availability windows (specialist\n timezone), then scores preferred-specialist, qualification level, on-call,\n workload headroom, and language (weights in `ROUTING_WEIGHTS`; every\n factor recorded so ineligible specialists show why); `autoAssign` writes\n the assignment with its rationale; `reassign` is gated by\n `support.reassign-case` and refuses cross-tenant principals.\n- **ServiceTimeEntryService** — the ONE recording contract for all four\n entry sources (FR-40): `record` validates duration (explicit or derived\n from the period; `timer` needs both bounds), participant coherence\n (`human` → `participantProfileId`, `agent` → `agentRef`), and the work\n context (case and/or work ref; case entries copy the tenant and append a\n `time_recorded` event); `submit` stamps the submitter.\n- **TimeEntryApprovalService** — the approval gate and only writer of the\n commercial snapshots (FR-36): `approve` resolves the governing terms (case\n `planSnapshot` first, live plan fallback, zero-rate default), gates by\n policy path (`automatic`/`threshold`/`operator`/`client`; operator paths\n behind `support.approve-time-entry`; cross-tenant principals refused), then\n derives the `SupportCharge` (included time first, then overage/on-call) and\n `SupportCompensation` (effective-dated plan) rows; `reject` and `correct`\n (supersede + linked fresh draft via `correctionOfId`) round out the\n lifecycle. Case events carry the charge amount only — compensation never\n leaks case-side.\n\n## Permissions\n\nContributed to the runtime catalog on import (the\n`personas.activate-directive` pattern): `support.reassign-case`,\n`support.approve-time-entry`, `support.manage-plans`. Privileged service\noperations take a `SupportPrincipal` (`can(slug)`), built from explicit slugs\nor a `PermissionResolver`.\n\n## Svelte UI\n\n`@happyvertical/smrt-support/svelte` ships presentational Svelte 5 surfaces:\n`CaseQueue` (priority/status/assignment/channel at a glance, `onselect`),\n`CaseDetail` (header, meta, linked work, merged timeline), `TargetList`\n(Service Target clocks with due/paused/satisfied/breached badges),\n`RoutingRationale` (ranked specialists with eligibility + factor chips and\nan optional reassign action), and `TimeEntryApprovalQueue`\n(date/hours/description/worker/status/amount with\napprove/reject-with-reason actions on submitted rows). Hosts adapt models\nwith `toSupportCaseView` / `toCaseTimelineItemView` / `toServiceTargetView`\n/ `toSupportTimeEntryView` — the time-entry view's first eight fields\ndeliberately match `smrt-projects`' presentation `TimeEntry` contract.\nComponents use `smrt-ui` primitives (`smrtRawPrimitives: \"strict\"`).\n\n## Dependencies\n\nPackage dependencies: `smrt-core`, `smrt-tenancy`, `smrt-projects`,\n`smrt-chat`, `smrt-messages`, `smrt-jobs`, `smrt-users`, `smrt-ui`,\n`@happyvertical/logger`, `@happyvertical/sql`. Profile and subscriptions\nreferences are `@crossPackageRef` string ids or plain string\nkeys. `smrt-projects` is the intentional shared delivery/evidence edge added\nby #1955. No inter-`smrt-*` `peerDependencies`.\n\n## Validation\n\n```sh\npnpm --filter @happyvertical/smrt-support test\npnpm --filter @happyvertical/smrt-support typecheck\npnpm --filter @happyvertical/smrt-support build\n```\n\n## Gotchas\n\n- **Writes go through the facades** — the case-side generated routes are\n read-only on purpose; `create`/`update` on them is not a bug to \"fix\".\n- **`projectId` is an app-defined string** — the consuming app owns the\n Project concept; don't turn it into an FK or a crossPackageRef to\n `smrt-projects:Project` (that class is a provider board, not a client\n initiative).\n- **`SupportCase.threadKey` + `SupportInteraction.sourceKey` carry intake\n correctness** — `sourceKey` is globally unique (`conflictColumns`), so\n replayed transport rows are no-ops; new manual interactions must use a\n fresh `manual:<uuid>` key.\n- **High-volume models avoid the inherited `name` field** (`subject`,\n `displayName`, …) so the default `(slug, context, _meta_type)` unique index\n falls back to id-derived slugs; named config models carry\n `conflictColumns` instead (which replaces that index).\n- **Approved time entries never change** — corrections are new rows linked\n by `correctionOfId`; the original flips to `corrected` with its snapshot\n intact.\n- **Plan edits never rewrite history** — cases keep `planSnapshot`, charges\n and compensation keep `rateSnapshot`.\n- **Create-or-join and included-time consumption serialize in-process** —\n `SupportIntakeService` (per conversation key) and\n `TimeEntryApprovalService` (per case) use a `KeyedMutex`, which covers the\n single-app-process deployment these paths run in. Multi-replica\n deployments must serialize at the app layer (route a conversation's\n intake / a case's approvals through one worker, or add an app-level\n claim) — the framework exposes no cross-adapter transaction primitive at\n this seam. Escalation is additionally idempotent per `(target, level)`,\n so at-least-once job redelivery never stacks duplicate escalations.\n"
5088
+ "agentDoc": "# @happyvertical/smrt-support\n\nAI-first Support Cases for the SMRT framework (epic #1934). Every inbound\nsupport interaction — chat message or email — creates or joins one canonical,\ntenant-scoped **Support Case**; channels stay transports, never separate\nsystems of record. The package owns the case lifecycle, channel intake, the\nAutomated Support Response workflow with lossless **Human Handoff**,\nProject-qualified routing, **Service Target** clocks with timed escalation,\nand auditable **Service Time Entries** whose client charges and provider\ncompensation stay separate. Since #1955 the canonical `ServiceTimeEntry` class\nis owned by `smrt-projects`; this package exposes a compatibility subtype that\nrestates the shared fields for isolated manifest scanning. Both imports use the\nunchanged `service_time_entries` table, so no data is duplicated.\n\n**Boundary (FR-29a):** Support owns tickets, interactions, routing, and\nservice targets. Delivery Operations owns repository work — a **Delivery\nHandoff** links a case to a Development Work Item (e.g.\n`@happyvertical/smrt-projects:Issue`) via a `SupportWorkLink` without ever\ndriving its execution; the delivery side reports status echoes back.\n\n## Models\n\nCase-side models are **internal**: generated API/CLI/MCP surfaces are\nread-only (`list`/`get`) and every write goes through the service facades\n(the `smrt-chat` `ChatService` idiom). Operational configuration models\n(bindings, policies, specialists, qualifications, availability) expose full\ngenerated CRUD; **commercial terms** (`SupportPlan`,\n`SupportCompensationPlan`) are read-only too — their writes flow through the\n`support.manage-plans`-gated `SupportPlanAdminService`.\n\n### Case core (#1926)\n\n- **SupportCase** (`support_cases`) — canonical ticket: `caseNumber`,\n `subject`, `status` (`new → triaged → assigned → in_progress →\n waiting_on_client|escalated → resolved → closed`, guarded at save time via\n the WeakMap + authoritative-DB-read idiom from S5 #1390), `priority`,\n `severity` (plan-defined key), `category`, `sensitive`, `channelKind`,\n `clientProfileId`/`openedByProfileId` (crossPackageRef → Profile),\n `projectId` (app-defined string — deliberately not an FK), `bindingId`,\n `threadKey` (create-or-join dedup key), `planId` + `planSnapshot` (terms\n frozen at apply time), assignment fields, response stamps\n (`acknowledgedAt`, `firstRespondedAt`), `humanRequestedAt`, resolution\n fields, `reopenCount`/`lastReopenedAt`, `aiEnabled`.\n- **SupportInteraction** (`support_interactions`,\n `conflictColumns: ['source_key']`) — channel-neutral transport record:\n `direction` (`inbound`/`outbound`/`internal`), `channelKind`, `actorKind`\n (`client`/`specialist`/`agent`/`system`), denormalized `body`, and the\n qualified `sourceType`/`sourceId` + globally unique `sourceKey`\n (`chat:<messageId>`, `email:<emailId>`, `manual:<uuid>`) that makes\n re-ingestion idempotent.\n- **SupportCaseEvent** (`support_case_events`) — append-only audit trail\n (transitions, assignments, AI runs, handoffs, escalations, target state,\n work links, reopens). Together with interactions this is the complete\n context a Human Handoff transfers.\n- **SupportChannelBinding** (`support_channel_bindings`,\n `conflictColumns: ['target_type','target_id']`) — marks a container as a\n support intake channel: `bindingKind` (`chat_room`/`email_account`),\n qualified `targetType` + `targetId`, default client/project/plan,\n `selfAddresses` (email direction heuristic). Unbound containers are never\n touched.\n- **SupportWorkLink** (`support_work_links`) — Support/Development Work Item\n links with a read-only `status` echo.\n\n### AI workflow (#1928)\n\n- **SupportPolicy** (`support_policies`) — what the AI may do without a\n human: `autoAcknowledge`/`autoClassify`/`autoAnswer` (on by default),\n `autoTroubleshoot`/`autoResolve` (off by default),\n `autoResolveMaxSeverity`, `confidenceThreshold`, `maxAutoAttempts`,\n `sensitiveCategories`, `allowedTools`, `autoSendEmailReplies` (off —\n email replies are drafted, not sent). Resolution is most-specific-wins on\n `(planId, projectId)`; no row → `DEFAULT_SUPPORT_POLICY` (conservative).\n- **SupportAiRun** (`support_ai_runs`) — append-only audit of each phase\n (`acknowledge`/`classify`/`answer`/`troubleshoot`/`resolve`) with\n `confidence`, classification output, knowledge refs, tool calls, outcome\n (`completed`/`skipped`/`failed`/`handed_off`), and `correlationId`.\n\n### Routing, targets, escalation (#1929)\n\n- **SupportSpecialist** (`support_specialists`) — specialist role record on a\n Profile (the commerce `Customer` role-record idiom): `languages`,\n `timezone`, `maxConcurrentCases`, `onCallPriority`, status.\n- **SupportQualification** (`support_qualifications`) — effective-dated\n Project Support Qualification (`trainee`/`qualified`/`expert`).\n- **SupportAvailability** (`support_availability_windows`) — weekly windows\n plus `on_call`/`time_off` spans.\n- **SupportPlan** (`support_plans`, `conflictColumns:\n ['tenant_id','plan_key']`) — the Managed Support Plan: coverage calendar +\n holidays + timezone, channels, severity definitions, per-severity\n acknowledgement/response/update/resolution target minutes, `pauseStatuses`\n (clocks pause only when the plan says so), escalation policy steps,\n availability fee, `includedMinutes`, `overageHourlyRate`,\n `onCallHourlyRate`, time-approval policy, optional\n `subscriptionPlanKey` binding (string, not a dependency).\n `snapshotTerms()` feeds `SupportCase.planSnapshot`.\n- **SupportServiceTarget** (`support_service_targets`,\n `conflictColumns: ['case_id','target_type','cycle']`) — one clock per\n target type (update clocks recur via `cycle`), with pause accounting and\n `escalationJobId` (the one-shot `_smrt_jobs` row scheduled at `dueAt`,\n cancelled on satisfy).\n- **SupportEscalation** (`support_escalations`) — the audit record of one\n escalation firing.\n- **SupportCompensationPlan** (`support_compensation_plans`) —\n effective-dated Support Specialist earning terms; specialist-specific plan\n wins over the tenant default at the work instant. Deliberately separate\n from `SupportPlan` (client pricing) so margin stays measurable.\n\n### Service time (#1930)\n\n- **ServiceTimeEntry** (`service_time_entries`) — auditable Professional\n Service duration: work-context-generic (`caseId` and/or polymorphic\n `workRefType`/`workRefId`), delivering Participant (`participantKind`\n `human`/`agent` + `participantProfileId`/`agentRef`), `source`\n (`timer`/`manual`/`import`/`agent` — one shared validation/audit\n contract), evidence JSON, and lifecycle `draft → submitted →\n approved|rejected`, `approved → corrected` only. **Approved entries are\n immutable**: the work-defining fields are frozen at save time (the\n `LicenseSale` WeakMap-freeze idiom, backed by an authoritative DB-row\n comparison); disputes create explicit corrections via `correctionOfId`.\n- **SupportCharge** / **SupportCompensation** (`support_charges` /\n `support_compensations`, one per time entry) — derived commercial\n snapshots: client amount from the Managed Support Plan (included-time\n consumption then metered overage) vs provider amount from the Support\n Compensation Plan, each with a frozen `rateSnapshot`. Margin = charge −\n compensation, computed by readers; settlement/invoicing composes at the\n app/accounting boundary (#1925) and is out of scope here.\n\n## Services\n\n- **SupportCaseService** — the closed write facade: `openCase`,\n `recordInteraction` (idempotent on `sourceKey`; stamps\n `acknowledgedAt`/`firstRespondedAt`; inbound un-parks\n `waiting_on_client`), `transition`, `assign`, `resolve`, `close`,\n `reopen` (preserves prior resolution in the reopen event), `requestHuman`,\n `linkWork`/`recordWorkStatus` (Delivery Handoff), `recordEvent`,\n `getTimeline` (merged interactions + events).\n- **SupportIntakeService** — create-or-join intake for both channels:\n `ingestChatMessage` (bound `ChatRoom`; `chat:<roomId>[:<threadId>]` thread\n keys; only `role: 'user'` messages), `ingestEmail` (bound `EmailAccount`;\n RFC-thread joins via `inReplyTo`; `selfAddresses` skip; unresolved senders\n park the case with `metadata.unresolvedClient`), plus\n `registerSupportIntake()` — an opt-in `GlobalInterceptors.afterSave`\n observer keyed on the `ChatMessage`/`Email` class names that never throws\n into the source package's save path. Join semantics: open case → join;\n resolved → reopen; closed → new case.\n- **SupportAiWorkflow** — the Automated Support Response (FR-28a):\n `processIntake`/`processCase` run acknowledge → classify → answer →\n resolve under the resolved `SupportPolicy` (conservative defaults), each\n phase writing a `SupportAiRun` + `ai_run` case event. AI calls go through\n the injected `SupportAiBoundary` (default uses `supportCase.do()` with\n defensive JSON parsing that fails toward the human); knowledge comes from\n the pluggable `SupportKnowledgeProvider` (default no-op). Classification\n never overwrites human triage; email answers are drafted\n (`metadata.draft`) unless the policy allows sending. Handoff triggers —\n `client_request`, `low_confidence`, `high_severity`, `sensitive`,\n `failed_resolution`, `policy` (attempt budget) — are always active.\n- **HumanHandoffService** — the lossless Human Handoff (FR-28b):\n `handoff` assembles the full context package (case state + compact\n timeline + AI runs) into the `handoff` event, stamps\n `metadata.activeHandoff` for the no-repeat guarantee (repeat triggers are\n deduped events), routes through the injected `assignSpecialist` seam\n (#1929's routing service plugs in; default queues `new` cases to\n `triaged`), and `releaseHandoff` clears the flag.\n- **ServiceTargetEngine** — Service Target clocks (FR-30/31):\n `startTargetsForCase` derives per-severity clocks from the case's\n `planSnapshot` (live plan fallback) with due times computed in covered\n time (`coverage-calendar.ts`: timezone-aware weekly windows + holidays;\n empty coverage = 24×7) and schedules one-shot `_smrt_jobs` escalation jobs\n at `dueAt`; `onInteractionRecorded` satisfies acknowledgement/response and\n recurs `update` cycles; `onCaseTransition` pauses/resumes clocks only when\n the plan's `pauseStatuses` say so (recomputing remaining covered minutes)\n and settles targets on resolve. Breach → `SupportServiceTarget.\n checkAndEscalate` (background-eligible, at-least-once-safe) marks the\n breach and escalates through the plan's policy steps (notify/reassign,\n delayed follow-up levels), transitioning the case to `escalated`.\n- **SupportPlanAdminService** — the permission-gated write surface for\n commercial terms: create/update/archive for Managed Support Plans and\n Support Compensation Plans, every act behind `support.manage-plans` with\n cross-tenant writes refused (generated CRUD on both models is read-only).\n- **SupportRoutingService** — explainable ranked routing (FR-30):\n `rankSpecialists` hard-filters on active status, effective Project\n Support Qualification, workload cap, and availability windows (specialist\n timezone), then scores preferred-specialist, qualification level, on-call,\n workload headroom, and language (weights in `ROUTING_WEIGHTS`; every\n factor recorded so ineligible specialists show why); `autoAssign` writes\n the assignment with its rationale; `reassign` is gated by\n `support.reassign-case` and refuses cross-tenant principals.\n- **ServiceTimeEntryService** — the ONE recording contract for all four\n entry sources (FR-40): `record` validates duration (explicit or derived\n from the period; `timer` needs both bounds), participant coherence\n (`human` → `participantProfileId`, `agent` → `agentRef`), and the work\n context (case and/or work ref; case entries copy the tenant and append a\n `time_recorded` event); `submit` stamps the submitter.\n- **TimeEntryApprovalService** — the approval gate and only writer of the\n commercial snapshots (FR-36): `approve` resolves the governing terms (case\n `planSnapshot` first, live plan fallback, zero-rate default), gates by\n policy path (`automatic`/`threshold`/`operator`/`client`; operator paths\n behind `support.approve-time-entry`; cross-tenant principals refused), then\n derives the `SupportCharge` (included time first, then overage/on-call) and\n `SupportCompensation` (effective-dated plan) rows; `reject` and `correct`\n (supersede + linked fresh draft via `correctionOfId`) round out the\n lifecycle. Case events carry the charge amount only — compensation never\n leaks case-side.\n\n## Permissions\n\nContributed to the runtime catalog on import (the\n`personas.activate-directive` pattern): `support.reassign-case`,\n`support.approve-time-entry`, `support.manage-plans`. Privileged service\noperations take a `SupportPrincipal` (`can(slug)`), built from explicit slugs\nor a `PermissionResolver`.\n\n## Svelte UI\n\n`@happyvertical/smrt-support/svelte` ships presentational Svelte 5 surfaces:\n`CaseQueue` (priority/status/assignment/channel at a glance, `onselect`),\n`CaseDetail` (header, meta, linked work, merged timeline), `TargetList`\n(Service Target clocks with due/paused/satisfied/breached badges),\n`RoutingRationale` (ranked specialists with eligibility + factor chips and\nan optional reassign action), and `TimeEntryApprovalQueue`\n(date/hours/description/worker/status/amount with\napprove/reject-with-reason actions on submitted rows). Hosts adapt models\nwith `toSupportCaseView` / `toCaseTimelineItemView` / `toServiceTargetView`\n/ `toSupportTimeEntryView` — the time-entry view's first eight fields\ndeliberately match `smrt-projects`' presentation `TimeEntry` contract.\nComponents use `smrt-ui` primitives (`smrtRawPrimitives: \"strict\"`).\n\n## Dependencies\n\nPackage dependencies: `smrt-core`, `smrt-tenancy`, `smrt-projects`,\n`smrt-chat`, `smrt-messages`, `smrt-jobs`, `smrt-users`, `smrt-ui`,\n`@happyvertical/logger`, `@happyvertical/sql`. Profile and subscriptions\nreferences are `@crossPackageRef` string ids or plain string\nkeys. `smrt-projects` is the intentional shared delivery/evidence edge added\nby #1955. No inter-`smrt-*` `peerDependencies`.\n\n## Validation\n\n```sh\npnpm --filter @happyvertical/smrt-support test\npnpm --filter @happyvertical/smrt-support test:postgres\npnpm --filter @happyvertical/smrt-support typecheck\npnpm --filter @happyvertical/smrt-support build\n```\n\n`test:postgres` is the lane that holds the money/rate line — SQLite's type\naffinity stores a REAL in an INTEGER column without complaint, so a money field\nthat reverted to DECIMAL passes every SQLite suite.\n\n## Money and rates\n\n- **Money is integer minor units** (cents) — `$19.99` is `1999`.\n `SupportPlan.availabilityFeeAmount`, `SupportCharge.amount` and\n `SupportCompensation.amount` initialize `= 0`, never `= 0.0`: the integer\n literal is what maps them to INTEGER columns (#2401).\n- **Rates stay DECIMAL but their unit changed.**\n `SupportPlan.overageHourlyRate` / `onCallHourlyRate` and\n `SupportCompensationPlan.hourlyRate` are **minor units per hour**, so\n `hours * rate` yields minor units. INTEGER would truncate a sub-unit rate to\n zero, which is why they keep a decimal initializer.\n- **`roundMoney()` is the single rate↔money boundary.** It rounds\n `(seconds / 3600) * hourlyRate` to a whole minor unit, which is what lets the\n `thresholdAmount` comparison in `determinePath()` be an exact `>` with no\n tolerance. `TimeApprovalPolicy.thresholdAmount` is in the same unit as\n `SupportCharge.amount`.\n- **Migrating an existing database**: `preflightSupportMoneyMinorUnits(db)`\n reports which columns still hold major units and which rows would be rounded\n or overflow `int4`; `migrateSupportMoneyToMinorUnits(db)` converts them\n (idempotent via `_smrt_backfills`). The **rate columns are not in that list**\n — their type does not change, but their stored values must still be\n multiplied by the same scale in the same deploy step. Frozen `rateSnapshot`\n JSON on settled charges is history and is left alone. On SQLite the values\n are rescaled but the declared type needs the table-rebuild path (#2370); the\n `int4` ceiling is ~2.1e9 minor units (~$21.4M) per column, with BIGINT parked\n in #2373.\n\n## Gotchas\n\n- **Writes go through the facades** — the case-side generated routes are\n read-only on purpose; `create`/`update` on them is not a bug to \"fix\".\n- **`projectId` is an app-defined string** — the consuming app owns the\n Project concept; don't turn it into an FK or a crossPackageRef to\n `smrt-projects:Project` (that class is a provider board, not a client\n initiative).\n- **`SupportCase.threadKey` + `SupportInteraction.sourceKey` carry intake\n correctness** — `sourceKey` is globally unique (`conflictColumns`), so\n replayed transport rows are no-ops; new manual interactions must use a\n fresh `manual:<uuid>` key.\n- **High-volume models avoid the inherited `name` field** (`subject`,\n `displayName`, …) so the default `(slug, context, _meta_type)` unique index\n falls back to id-derived slugs; named config models carry\n `conflictColumns` instead (which replaces that index).\n- **Approved time entries never change** — corrections are new rows linked\n by `correctionOfId`; the original flips to `corrected` with its snapshot\n intact.\n- **Plan edits never rewrite history** — cases keep `planSnapshot`, charges\n and compensation keep `rateSnapshot`.\n- **Create-or-join and included-time consumption serialize in-process** —\n `SupportIntakeService` (per conversation key) and\n `TimeEntryApprovalService` (per case) use a `KeyedMutex`, which covers the\n single-app-process deployment these paths run in. Multi-replica\n deployments must serialize at the app layer (route a conversation's\n intake / a case's approvals through one worker, or add an app-level\n claim) — the framework exposes no cross-adapter transaction primitive at\n this seam. Escalation is additionally idempotent per `(target, level)`,\n so at-least-once job redelivery never stacks duplicate escalations.\n"
5059
5089
  }
@@ -55,8 +55,23 @@ function formatHours(hours: number): string {
55
55
  return `${hours.toFixed(1)}h`;
56
56
  }
57
57
 
58
- function formatAmount(amount: number): string {
59
- return amount.toFixed(2);
58
+ /**
59
+ * Render a minor-units charge for display.
60
+ *
61
+ * `amount` is integer minor units (#2401), so `toFixed(2)` would print 180
62
+ * cents as "180.00" — a hundredfold overstatement. The scale is the currency's
63
+ * own minor-unit exponent rather than a hard-coded 100, so a zero-decimal
64
+ * currency (JPY, KRW) is not divided at all and a three-decimal one (BHD) is
65
+ * divided by 1000. No currency symbol is added: the queue shows a bare figure
66
+ * and the host app supplies the label.
67
+ */
68
+ function formatAmount(amount: number, currency = 'USD'): string {
69
+ const digits =
70
+ new Intl.NumberFormat(undefined, {
71
+ style: 'currency',
72
+ currency,
73
+ }).resolvedOptions().maximumFractionDigits ?? 2;
74
+ return (amount / 10 ** digits).toFixed(digits);
60
75
  }
61
76
  </script>
62
77
 
@@ -83,7 +98,7 @@ function formatAmount(amount: number): string {
83
98
  />
84
99
  {#if entry.amount !== undefined}
85
100
  <span class="time-approval-amount">
86
- {formatAmount(entry.amount)}
101
+ {formatAmount(entry.amount, entry.currency)}
87
102
  </span>
88
103
  {/if}
89
104
  </span>
@@ -1 +1 @@
1
- {"version":3,"file":"TimeEntryApprovalQueue.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryApprovalQueue.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAmGD,QAAA,MAAM,sBAAsB,iEAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
1
+ {"version":3,"file":"TimeEntryApprovalQueue.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/TimeEntryApprovalQueue.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,EAEL,KAAK,oBAAoB,EAE1B,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,oBAAoB,EAAE,CAAC;IAChC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAkHD,QAAA,MAAM,sBAAsB,iEAAwC,CAAC;AACrE,KAAK,sBAAsB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC;AACxE,eAAe,sBAAsB,CAAC"}
@@ -14,9 +14,10 @@ function entryView(overrides = {}) {
14
14
  hours: 1.5,
15
15
  description: 'Debugged the login loop',
16
16
  status: 'submitted',
17
- amount: 180,
17
+ amount: 18000, // $180.00 in cents (#2401)
18
+ currency: 'USD',
18
19
  workerName: 'Ada',
19
- hourlyRate: 120,
20
+ hourlyRate: 12000, // $120.00/h in cents
20
21
  source: 'timer',
21
22
  participantKind: 'human',
22
23
  caseId: 'case-1',
@@ -42,6 +43,15 @@ describe('TimeEntryApprovalQueue', () => {
42
43
  });
43
44
  expect(screen.queryByText('180.00')).not.toBeInTheDocument();
44
45
  });
46
+ it('scales by the currency exponent, not a hard-coded 100', () => {
47
+ // ¥18000 is 18000 minor units — JPY has no minor unit — so dividing by 100
48
+ // here would understate the charge a hundredfold (#2401).
49
+ render(TimeEntryApprovalQueue, {
50
+ props: { entries: [entryView({ currency: 'JPY' })] },
51
+ });
52
+ expect(screen.getByText('18000')).toBeInTheDocument();
53
+ expect(screen.queryByText('180.00')).not.toBeInTheDocument();
54
+ });
45
55
  it('invokes onapprove with the entry id', async () => {
46
56
  const onapprove = vi.fn();
47
57
  render(TimeEntryApprovalQueue, {
@@ -60,10 +60,23 @@ export interface SupportTimeEntryView {
60
60
  hours: number;
61
61
  description: string;
62
62
  status: string;
63
- /** Client charge amount, when a derived {@link SupportCharge} is supplied. */
63
+ /**
64
+ * Client charge in integer minor units, when a derived
65
+ * {@link SupportCharge} is supplied — `$19.99` is `1999` (#2401).
66
+ */
64
67
  amount?: number;
68
+ /**
69
+ * ISO 4217 code `amount` / `hourlyRate` are denominated in, when a charge is
70
+ * supplied. Carried on the view because the minor-unit exponent is a property
71
+ * of the currency, not a constant: `¥1999` is 1999 minor units, not 19.99
72
+ * (#2401).
73
+ */
74
+ currency?: string;
65
75
  workerName?: string;
66
- /** Hourly rate from the charge's frozen `rateSnapshot`, when supplied. */
76
+ /**
77
+ * Hourly rate from the charge's frozen `rateSnapshot`, when supplied, in
78
+ * minor units per hour (#2401).
79
+ */
67
80
  hourlyRate?: number;
68
81
  source: string;
69
82
  participantKind: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/svelte/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/svelte/types.ts"],"names":[],"mappings":"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"}
@@ -116,6 +116,7 @@ export function toSupportTimeEntryView(entry, opts = {}) {
116
116
  };
117
117
  if (opts.charge) {
118
118
  view.amount = opts.charge.amount;
119
+ view.currency = opts.charge.currency;
119
120
  const hourlyRate = opts.charge.getRateSnapshot().hourlyRate;
120
121
  if (typeof hourlyRate === 'number') {
121
122
  view.hourlyRate = hourlyRate;
package/dist/types.d.ts CHANGED
@@ -109,7 +109,11 @@ export interface TimeApprovalPolicy {
109
109
  mode: 'automatic' | 'operator' | 'client';
110
110
  /** Entries longer than this require review even under `automatic`. */
111
111
  thresholdMinutes?: number;
112
- /** Entries pricing above this amount require review even under `automatic`. */
112
+ /**
113
+ * Entries pricing above this amount require review even under `automatic`.
114
+ * Integer minor units, matching `SupportCharge.amount` it is compared
115
+ * against — `$19.99` is `1999` (#2401).
116
+ */
113
117
  thresholdAmount?: number;
114
118
  }
115
119
  /** One piece of work evidence attached to a Service Time Entry. */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/**\n * Shared types, enums, and lifecycle maps for `@happyvertical/smrt-support`.\n *\n * Domain language follows the HappyVertical support glossary: a **Support\n * Case** is the canonical record of a client support request; **Support\n * Interactions** are channel transports attached to it; **Service Targets**\n * are operational clocks (not contractual SLAs); a **Managed Support Plan**\n * carries the client-facing terms while a **Support Compensation Plan**\n * carries the provider-facing terms — the two are never merged.\n */\n\n/** Support Case lifecycle states (FR-29b). */\nexport type SupportCaseStatus =\n | 'new'\n | 'triaged'\n | 'assigned'\n | 'in_progress'\n | 'waiting_on_client'\n | 'escalated'\n | 'resolved'\n | 'closed';\n\n/**\n * Legal Support Case status transitions, enforced at save time by\n * {@link SupportCase} (S5 guarded-lifecycle idiom — raw mass-assignment on a\n * generated route cannot skip states). Reopening (`resolved`/`closed` back to\n * an active state) is legal here; the reopen bookkeeping (count, history\n * event) is layered by `SupportCaseService.reopen()`.\n */\nexport const SUPPORT_CASE_STATUS_TRANSITIONS: Record<\n SupportCaseStatus,\n SupportCaseStatus[]\n> = {\n new: [\n 'triaged',\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n triaged: [\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n assigned: [\n 'triaged',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n in_progress: [\n 'assigned',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n waiting_on_client: [\n 'assigned',\n 'in_progress',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n escalated: [\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'resolved',\n 'closed',\n ],\n resolved: ['closed', 'in_progress', 'triaged'],\n closed: ['triaged', 'in_progress'],\n};\n\n/** Case statuses considered \"open\" for create-or-join intake dedup. */\nexport const OPEN_SUPPORT_CASE_STATUSES: SupportCaseStatus[] = [\n 'new',\n 'triaged',\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n];\n\n/** Client-facing priority (ordering/urgency inside a queue). */\nexport type SupportCasePriority = 'low' | 'normal' | 'high' | 'urgent';\n\n/** Transport kind a Support Interaction arrived through. */\nexport type SupportChannelKind =\n | 'chat'\n | 'email'\n | 'phone'\n | 'voice'\n | 'web'\n | 'api';\n\n/** Direction of a Support Interaction relative to the Client. */\nexport type SupportInteractionDirection = 'inbound' | 'outbound' | 'internal';\n\n/** Who performed an interaction or case event. */\nexport type SupportActorKind = 'client' | 'specialist' | 'agent' | 'system';\n\n/** Append-only case audit event types. */\nexport type SupportCaseEventType =\n | 'created'\n | 'interaction'\n | 'transition'\n | 'assignment'\n | 'ai_run'\n | 'handoff'\n | 'human_requested'\n | 'escalation'\n | 'target_scheduled'\n | 'target_satisfied'\n | 'target_breached'\n | 'target_paused'\n | 'target_resumed'\n | 'work_linked'\n | 'reopened'\n | 'plan_applied'\n | 'time_recorded'\n | 'note';\n\n/** What kind of transport container a channel binding points at. */\nexport type SupportBindingKind = 'chat_room' | 'email_account';\n\n/** Operational Work Item link kinds carried by a {@link SupportWorkLink}. */\nexport type SupportWorkLinkKind = 'support_work_item' | 'development_work_item';\n\n/** Service Target clock types (FR-30/FR-31). */\nexport type ServiceTargetType =\n | 'acknowledgement'\n | 'response'\n | 'update'\n | 'resolution';\n\n/** Service Target clock states. */\nexport type ServiceTargetStatus =\n | 'pending'\n | 'paused'\n | 'satisfied'\n | 'breached'\n | 'cancelled';\n\n/** Why a case was escalated. */\nexport type SupportEscalationReason =\n | 'target_breach'\n | 'target_risk'\n | 'manual'\n | 'ai_trigger';\n\n/** AI support workflow phases (FR-28a). */\nexport type SupportAiRunPhase =\n | 'acknowledge'\n | 'classify'\n | 'answer'\n | 'troubleshoot'\n | 'resolve';\n\n/** Terminal outcome of one AI workflow phase run. */\nexport type SupportAiRunOutcome =\n | 'completed'\n | 'skipped'\n | 'failed'\n | 'handed_off';\n\n/** What triggered a Human Handoff (FR-28b). */\nexport type HumanHandoffTrigger =\n | 'client_request'\n | 'low_confidence'\n | 'high_severity'\n | 'sensitive'\n | 'failed_resolution'\n | 'target_risk'\n | 'policy'\n | 'manual';\n\n/** Service Time Entry lifecycle (FR-36). */\nexport type ServiceTimeEntryStatus =\n | 'draft'\n | 'submitted'\n | 'approved'\n | 'rejected'\n | 'corrected';\n\n/**\n * Legal Service Time Entry status transitions. `approved → corrected` is the\n * only exit from `approved` and is reachable exclusively through the explicit\n * correction path (the approved snapshot itself stays frozen).\n */\nexport const SERVICE_TIME_ENTRY_STATUS_TRANSITIONS: Record<\n ServiceTimeEntryStatus,\n ServiceTimeEntryStatus[]\n> = {\n draft: ['submitted', 'rejected'],\n submitted: ['approved', 'rejected', 'draft'],\n approved: ['corrected'],\n rejected: ['draft', 'submitted'],\n corrected: [],\n};\n\n/** How a Service Time Entry was produced (FR-40). */\nexport type ServiceTimeEntrySource = 'timer' | 'manual' | 'import' | 'agent';\n\n/** Who/what delivered the work behind a Service Time Entry. */\nexport type SupportParticipantKind = 'human' | 'agent';\n\n/** Which approval path accepted a Service Time Entry (FR-36). */\nexport type TimeEntryApprovalPath =\n | 'automatic'\n | 'operator'\n | 'client'\n | 'threshold';\n\n/** Derived commercial snapshot lifecycle (charges and compensation). */\nexport type SupportSettlementStatus =\n | 'pending'\n | 'final'\n | 'corrected'\n | 'voided';\n\n/** Support Specialist availability window kinds. */\nexport type SupportAvailabilityKind = 'weekly' | 'on_call' | 'time_off';\n\n/** Support Specialist activation state. */\nexport type SupportSpecialistStatus = 'active' | 'inactive';\n\n/** Project Support Qualification levels. */\nexport type SupportQualificationLevel = 'trainee' | 'qualified' | 'expert';\n\n/** A weekly coverage window in a plan's coverage calendar. */\nexport interface CoverageWindow {\n /** 0 = Sunday … 6 = Saturday (JS `Date#getDay` convention). */\n weekday: number;\n /** Minutes from midnight, inclusive start. */\n startMinute: number;\n /** Minutes from midnight, exclusive end. */\n endMinute: number;\n}\n\n/** Per-severity Service Target minutes; `null` disables that clock. */\nexport interface ServiceTargetMinutes {\n acknowledgementMinutes: number | null;\n responseMinutes: number | null;\n updateMinutes: number | null;\n resolutionMinutes: number | null;\n}\n\n/** One step of a plan's escalation policy, applied in `level` order. */\nexport interface EscalationStep {\n level: number;\n action: 'notify' | 'reassign';\n /** Profile ids to notify (recorded on the escalation; delivery is app-side). */\n notifyProfileIds?: string[];\n /** Minutes after the previous step before this one fires (0 = immediately). */\n delayMinutes?: number;\n}\n\n/** A severity definition entry in a plan (key → labels). */\nexport interface SeverityDefinition {\n label: string;\n description?: string;\n}\n\n/**\n * Default severity vocabulary used when a plan doesn't define its own.\n * `sev1` is the most severe.\n */\nexport const DEFAULT_SEVERITY_DEFINITIONS: Record<string, SeverityDefinition> =\n {\n sev1: { label: 'Critical', description: 'Service down or unusable' },\n sev2: { label: 'Major', description: 'Significant degradation' },\n sev3: { label: 'Minor', description: 'Limited impact' },\n sev4: { label: 'Question', description: 'Question or request' },\n };\n\n/** Time-entry approval policy carried by a Managed Support Plan (FR-36). */\nexport interface TimeApprovalPolicy {\n /** Base path when no threshold applies. */\n mode: 'automatic' | 'operator' | 'client';\n /** Entries longer than this require review even under `automatic`. */\n thresholdMinutes?: number;\n /** Entries pricing above this amount require review even under `automatic`. */\n thresholdAmount?: number;\n}\n\n/** One piece of work evidence attached to a Service Time Entry. */\nexport interface TimeEntryEvidence {\n kind: string;\n ref: string;\n label?: string;\n}\n\n/**\n * Parse a JSON value stored as text, returning the fallback on malformed\n * input (never throws — stored JSON may predate schema changes). Accepts an\n * already-parsed value (e.g. a `type: 'json'` column that hydrated as an\n * object) and hands it back unchanged.\n */\nexport function parseJsonField<T>(raw: unknown, fallback: T): T {\n if (!raw) {\n return fallback;\n }\n // Text columns hydrate as strings; a non-string here means the caller\n // already holds a parsed value — hand it back rather than throwing.\n if (typeof raw !== 'string') {\n return raw as T;\n }\n try {\n const parsed = JSON.parse(raw);\n return (parsed ?? fallback) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Parse a JSON array-of-strings field, tolerating malformed input. */\nexport function parseStringArrayField(\n raw: string | null | undefined,\n): string[] {\n const parsed = parseJsonField<unknown>(raw, []);\n return Array.isArray(parsed)\n ? parsed.filter((value): value is string => typeof value === 'string')\n : [];\n}\n"],"mappings":";AA6BO,IAAM,kCAGT;CACF,KAAK;EACH;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;CACF;CACA,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;CACF;CACA,aAAa;EACX;EACA;EACA;EACA;EACA;CACF;CACA,mBAAmB;EACjB;EACA;EACA;EACA;EACA;CACF;CACA,WAAW;EACT;EACA;EACA;EACA;EACA;CACF;CACA,UAAU;EAAC;EAAU;EAAe;CAAS;CAC7C,QAAQ,CAAC,WAAW,aAAa;AACnC;AAGO,IAAM,6BAAkD;CAC7D;CACA;CACA;CACA;CACA;CACA;AACF;AA4GO,IAAM,wCAGT;CACF,OAAO,CAAC,aAAa,UAAU;CAC/B,WAAW;EAAC;EAAY;EAAY;CAAO;CAC3C,UAAU,CAAC,WAAW;CACtB,UAAU,CAAC,SAAS,WAAW;CAC/B,WAAW,CAAC;AACd;AAqEO,IAAM,+BACX;CACE,MAAM;EAAE,OAAO;EAAY,aAAa;CAA2B;CACnE,MAAM;EAAE,OAAO;EAAS,aAAa;CAA0B;CAC/D,MAAM;EAAE,OAAO;EAAS,aAAa;CAAiB;CACtD,MAAM;EAAE,OAAO;EAAY,aAAa;CAAsB;AAChE;AAyBK,SAAS,eAAkB,KAAc,UAAgB;CAC9D,IAAI,CAAC,KACH,OAAO;CAIT,IAAI,OAAO,QAAQ,UACjB,OAAO;CAET,IAAI;EAEF,OADe,KAAK,MAAM,GAClB,KAAU;CACpB,QAAQ;EACN,OAAO;CACT;AACF;AAGO,SAAS,sBACd,KACU;CACV,MAAM,SAAS,eAAwB,KAAK,CAAC,CAAC;CAC9C,OAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACnE,CAAC;AACP"}
1
+ {"version":3,"file":"types.js","names":[],"sources":["../src/types.ts"],"sourcesContent":["/**\n * Shared types, enums, and lifecycle maps for `@happyvertical/smrt-support`.\n *\n * Domain language follows the HappyVertical support glossary: a **Support\n * Case** is the canonical record of a client support request; **Support\n * Interactions** are channel transports attached to it; **Service Targets**\n * are operational clocks (not contractual SLAs); a **Managed Support Plan**\n * carries the client-facing terms while a **Support Compensation Plan**\n * carries the provider-facing terms — the two are never merged.\n */\n\n/** Support Case lifecycle states (FR-29b). */\nexport type SupportCaseStatus =\n | 'new'\n | 'triaged'\n | 'assigned'\n | 'in_progress'\n | 'waiting_on_client'\n | 'escalated'\n | 'resolved'\n | 'closed';\n\n/**\n * Legal Support Case status transitions, enforced at save time by\n * {@link SupportCase} (S5 guarded-lifecycle idiom — raw mass-assignment on a\n * generated route cannot skip states). Reopening (`resolved`/`closed` back to\n * an active state) is legal here; the reopen bookkeeping (count, history\n * event) is layered by `SupportCaseService.reopen()`.\n */\nexport const SUPPORT_CASE_STATUS_TRANSITIONS: Record<\n SupportCaseStatus,\n SupportCaseStatus[]\n> = {\n new: [\n 'triaged',\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n triaged: [\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n assigned: [\n 'triaged',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n in_progress: [\n 'assigned',\n 'waiting_on_client',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n waiting_on_client: [\n 'assigned',\n 'in_progress',\n 'escalated',\n 'resolved',\n 'closed',\n ],\n escalated: [\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'resolved',\n 'closed',\n ],\n resolved: ['closed', 'in_progress', 'triaged'],\n closed: ['triaged', 'in_progress'],\n};\n\n/** Case statuses considered \"open\" for create-or-join intake dedup. */\nexport const OPEN_SUPPORT_CASE_STATUSES: SupportCaseStatus[] = [\n 'new',\n 'triaged',\n 'assigned',\n 'in_progress',\n 'waiting_on_client',\n 'escalated',\n];\n\n/** Client-facing priority (ordering/urgency inside a queue). */\nexport type SupportCasePriority = 'low' | 'normal' | 'high' | 'urgent';\n\n/** Transport kind a Support Interaction arrived through. */\nexport type SupportChannelKind =\n | 'chat'\n | 'email'\n | 'phone'\n | 'voice'\n | 'web'\n | 'api';\n\n/** Direction of a Support Interaction relative to the Client. */\nexport type SupportInteractionDirection = 'inbound' | 'outbound' | 'internal';\n\n/** Who performed an interaction or case event. */\nexport type SupportActorKind = 'client' | 'specialist' | 'agent' | 'system';\n\n/** Append-only case audit event types. */\nexport type SupportCaseEventType =\n | 'created'\n | 'interaction'\n | 'transition'\n | 'assignment'\n | 'ai_run'\n | 'handoff'\n | 'human_requested'\n | 'escalation'\n | 'target_scheduled'\n | 'target_satisfied'\n | 'target_breached'\n | 'target_paused'\n | 'target_resumed'\n | 'work_linked'\n | 'reopened'\n | 'plan_applied'\n | 'time_recorded'\n | 'note';\n\n/** What kind of transport container a channel binding points at. */\nexport type SupportBindingKind = 'chat_room' | 'email_account';\n\n/** Operational Work Item link kinds carried by a {@link SupportWorkLink}. */\nexport type SupportWorkLinkKind = 'support_work_item' | 'development_work_item';\n\n/** Service Target clock types (FR-30/FR-31). */\nexport type ServiceTargetType =\n | 'acknowledgement'\n | 'response'\n | 'update'\n | 'resolution';\n\n/** Service Target clock states. */\nexport type ServiceTargetStatus =\n | 'pending'\n | 'paused'\n | 'satisfied'\n | 'breached'\n | 'cancelled';\n\n/** Why a case was escalated. */\nexport type SupportEscalationReason =\n | 'target_breach'\n | 'target_risk'\n | 'manual'\n | 'ai_trigger';\n\n/** AI support workflow phases (FR-28a). */\nexport type SupportAiRunPhase =\n | 'acknowledge'\n | 'classify'\n | 'answer'\n | 'troubleshoot'\n | 'resolve';\n\n/** Terminal outcome of one AI workflow phase run. */\nexport type SupportAiRunOutcome =\n | 'completed'\n | 'skipped'\n | 'failed'\n | 'handed_off';\n\n/** What triggered a Human Handoff (FR-28b). */\nexport type HumanHandoffTrigger =\n | 'client_request'\n | 'low_confidence'\n | 'high_severity'\n | 'sensitive'\n | 'failed_resolution'\n | 'target_risk'\n | 'policy'\n | 'manual';\n\n/** Service Time Entry lifecycle (FR-36). */\nexport type ServiceTimeEntryStatus =\n | 'draft'\n | 'submitted'\n | 'approved'\n | 'rejected'\n | 'corrected';\n\n/**\n * Legal Service Time Entry status transitions. `approved → corrected` is the\n * only exit from `approved` and is reachable exclusively through the explicit\n * correction path (the approved snapshot itself stays frozen).\n */\nexport const SERVICE_TIME_ENTRY_STATUS_TRANSITIONS: Record<\n ServiceTimeEntryStatus,\n ServiceTimeEntryStatus[]\n> = {\n draft: ['submitted', 'rejected'],\n submitted: ['approved', 'rejected', 'draft'],\n approved: ['corrected'],\n rejected: ['draft', 'submitted'],\n corrected: [],\n};\n\n/** How a Service Time Entry was produced (FR-40). */\nexport type ServiceTimeEntrySource = 'timer' | 'manual' | 'import' | 'agent';\n\n/** Who/what delivered the work behind a Service Time Entry. */\nexport type SupportParticipantKind = 'human' | 'agent';\n\n/** Which approval path accepted a Service Time Entry (FR-36). */\nexport type TimeEntryApprovalPath =\n | 'automatic'\n | 'operator'\n | 'client'\n | 'threshold';\n\n/** Derived commercial snapshot lifecycle (charges and compensation). */\nexport type SupportSettlementStatus =\n | 'pending'\n | 'final'\n | 'corrected'\n | 'voided';\n\n/** Support Specialist availability window kinds. */\nexport type SupportAvailabilityKind = 'weekly' | 'on_call' | 'time_off';\n\n/** Support Specialist activation state. */\nexport type SupportSpecialistStatus = 'active' | 'inactive';\n\n/** Project Support Qualification levels. */\nexport type SupportQualificationLevel = 'trainee' | 'qualified' | 'expert';\n\n/** A weekly coverage window in a plan's coverage calendar. */\nexport interface CoverageWindow {\n /** 0 = Sunday … 6 = Saturday (JS `Date#getDay` convention). */\n weekday: number;\n /** Minutes from midnight, inclusive start. */\n startMinute: number;\n /** Minutes from midnight, exclusive end. */\n endMinute: number;\n}\n\n/** Per-severity Service Target minutes; `null` disables that clock. */\nexport interface ServiceTargetMinutes {\n acknowledgementMinutes: number | null;\n responseMinutes: number | null;\n updateMinutes: number | null;\n resolutionMinutes: number | null;\n}\n\n/** One step of a plan's escalation policy, applied in `level` order. */\nexport interface EscalationStep {\n level: number;\n action: 'notify' | 'reassign';\n /** Profile ids to notify (recorded on the escalation; delivery is app-side). */\n notifyProfileIds?: string[];\n /** Minutes after the previous step before this one fires (0 = immediately). */\n delayMinutes?: number;\n}\n\n/** A severity definition entry in a plan (key → labels). */\nexport interface SeverityDefinition {\n label: string;\n description?: string;\n}\n\n/**\n * Default severity vocabulary used when a plan doesn't define its own.\n * `sev1` is the most severe.\n */\nexport const DEFAULT_SEVERITY_DEFINITIONS: Record<string, SeverityDefinition> =\n {\n sev1: { label: 'Critical', description: 'Service down or unusable' },\n sev2: { label: 'Major', description: 'Significant degradation' },\n sev3: { label: 'Minor', description: 'Limited impact' },\n sev4: { label: 'Question', description: 'Question or request' },\n };\n\n/** Time-entry approval policy carried by a Managed Support Plan (FR-36). */\nexport interface TimeApprovalPolicy {\n /** Base path when no threshold applies. */\n mode: 'automatic' | 'operator' | 'client';\n /** Entries longer than this require review even under `automatic`. */\n thresholdMinutes?: number;\n /**\n * Entries pricing above this amount require review even under `automatic`.\n * Integer minor units, matching `SupportCharge.amount` it is compared\n * against — `$19.99` is `1999` (#2401).\n */\n thresholdAmount?: number;\n}\n\n/** One piece of work evidence attached to a Service Time Entry. */\nexport interface TimeEntryEvidence {\n kind: string;\n ref: string;\n label?: string;\n}\n\n/**\n * Parse a JSON value stored as text, returning the fallback on malformed\n * input (never throws — stored JSON may predate schema changes). Accepts an\n * already-parsed value (e.g. a `type: 'json'` column that hydrated as an\n * object) and hands it back unchanged.\n */\nexport function parseJsonField<T>(raw: unknown, fallback: T): T {\n if (!raw) {\n return fallback;\n }\n // Text columns hydrate as strings; a non-string here means the caller\n // already holds a parsed value — hand it back rather than throwing.\n if (typeof raw !== 'string') {\n return raw as T;\n }\n try {\n const parsed = JSON.parse(raw);\n return (parsed ?? fallback) as T;\n } catch {\n return fallback;\n }\n}\n\n/** Parse a JSON array-of-strings field, tolerating malformed input. */\nexport function parseStringArrayField(\n raw: string | null | undefined,\n): string[] {\n const parsed = parseJsonField<unknown>(raw, []);\n return Array.isArray(parsed)\n ? parsed.filter((value): value is string => typeof value === 'string')\n : [];\n}\n"],"mappings":";AA6BO,IAAM,kCAGT;CACF,KAAK;EACH;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CACA,SAAS;EACP;EACA;EACA;EACA;EACA;EACA;CACF;CACA,UAAU;EACR;EACA;EACA;EACA;EACA;EACA;CACF;CACA,aAAa;EACX;EACA;EACA;EACA;EACA;CACF;CACA,mBAAmB;EACjB;EACA;EACA;EACA;EACA;CACF;CACA,WAAW;EACT;EACA;EACA;EACA;EACA;CACF;CACA,UAAU;EAAC;EAAU;EAAe;CAAS;CAC7C,QAAQ,CAAC,WAAW,aAAa;AACnC;AAGO,IAAM,6BAAkD;CAC7D;CACA;CACA;CACA;CACA;CACA;AACF;AA4GO,IAAM,wCAGT;CACF,OAAO,CAAC,aAAa,UAAU;CAC/B,WAAW;EAAC;EAAY;EAAY;CAAO;CAC3C,UAAU,CAAC,WAAW;CACtB,UAAU,CAAC,SAAS,WAAW;CAC/B,WAAW,CAAC;AACd;AAqEO,IAAM,+BACX;CACE,MAAM;EAAE,OAAO;EAAY,aAAa;CAA2B;CACnE,MAAM;EAAE,OAAO;EAAS,aAAa;CAA0B;CAC/D,MAAM;EAAE,OAAO;EAAS,aAAa;CAAiB;CACtD,MAAM;EAAE,OAAO;EAAY,aAAa;CAAsB;AAChE;AA6BK,SAAS,eAAkB,KAAc,UAAgB;CAC9D,IAAI,CAAC,KACH,OAAO;CAIT,IAAI,OAAO,QAAQ,UACjB,OAAO;CAET,IAAI;EAEF,OADe,KAAK,MAAM,GAClB,KAAU;CACpB,QAAQ;EACN,OAAO;CACT;AACF;AAGO,SAAS,sBACd,KACU;CACV,MAAM,SAAS,eAAwB,KAAK,CAAC,CAAC;CAC9C,OAAO,MAAM,QAAQ,MAAM,IACvB,OAAO,QAAQ,UAA2B,OAAO,UAAU,QAAQ,IACnE,CAAC;AACP"}