@d19n/youfibre-odin-sdk 2.0.166 → 2.0.168
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/dist/actions-v2/CreateInboundCallEventDto.d.ts +10 -9
- package/dist/actions-v2/CreateOutboundCallEventDto.d.ts +10 -9
- package/dist/actions-v2/UpdateCallEventAfterProcessingDto.d.ts +1 -22
- package/dist/process-model/index.d.ts +14 -0
- package/dist/process-model/index.js +33 -0
- package/dist/process-model/install-process.projection.d.ts +17 -0
- package/dist/process-model/install-process.projection.js +56 -0
- package/dist/process-model/order-process.projection.d.ts +2 -0
- package/dist/process-model/order-process.projection.js +289 -0
- package/dist/process-model/process-validator.d.ts +78 -0
- package/dist/process-model/process-validator.js +509 -0
- package/dist/process-model/types.d.ts +185 -0
- package/dist/process-model/types.js +20 -0
- package/package.json +11 -1
|
@@ -22,8 +22,8 @@ export interface CreateInboundCallEventMessage extends OdinRecordCreatedEvent<Cr
|
|
|
22
22
|
/**
|
|
23
23
|
* Properties for CreateInboundCallEvent action
|
|
24
24
|
*
|
|
25
|
-
* @property Required fields:
|
|
26
|
-
* @property Optional fields:
|
|
25
|
+
* @property Required fields: 6
|
|
26
|
+
* @property Optional fields: 2
|
|
27
27
|
*/
|
|
28
28
|
export interface CreateInboundCallEventProperties {
|
|
29
29
|
/**
|
|
@@ -66,6 +66,14 @@ export interface CreateInboundCallEventProperties {
|
|
|
66
66
|
* @required
|
|
67
67
|
*/
|
|
68
68
|
ProcessingStatus: string;
|
|
69
|
+
/**
|
|
70
|
+
* Title
|
|
71
|
+
*
|
|
72
|
+
* Title of twilio call event record
|
|
73
|
+
* @type {TEXT}
|
|
74
|
+
* @required
|
|
75
|
+
*/
|
|
76
|
+
Title: string;
|
|
69
77
|
/**
|
|
70
78
|
* CallId
|
|
71
79
|
*
|
|
@@ -80,13 +88,6 @@ export interface CreateInboundCallEventProperties {
|
|
|
80
88
|
* @type {TEXT}
|
|
81
89
|
*/
|
|
82
90
|
CallSid?: string | null;
|
|
83
|
-
/**
|
|
84
|
-
* Title
|
|
85
|
-
*
|
|
86
|
-
* Title of twilio call event record
|
|
87
|
-
* @type {TEXT}
|
|
88
|
-
*/
|
|
89
|
-
Title?: string | null;
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* CreateInboundCallEvent
|
|
@@ -22,8 +22,8 @@ export interface CreateOutboundCallEventMessage extends OdinRecordCreatedEvent<C
|
|
|
22
22
|
/**
|
|
23
23
|
* Properties for CreateOutboundCallEvent action
|
|
24
24
|
*
|
|
25
|
-
* @property Required fields:
|
|
26
|
-
* @property Optional fields:
|
|
25
|
+
* @property Required fields: 6
|
|
26
|
+
* @property Optional fields: 2
|
|
27
27
|
*/
|
|
28
28
|
export interface CreateOutboundCallEventProperties {
|
|
29
29
|
/**
|
|
@@ -66,6 +66,14 @@ export interface CreateOutboundCallEventProperties {
|
|
|
66
66
|
* @required
|
|
67
67
|
*/
|
|
68
68
|
ProcessingStatus: string;
|
|
69
|
+
/**
|
|
70
|
+
* Title
|
|
71
|
+
*
|
|
72
|
+
* Title of twilio call event record
|
|
73
|
+
* @type {TEXT}
|
|
74
|
+
* @required
|
|
75
|
+
*/
|
|
76
|
+
Title: string;
|
|
69
77
|
/**
|
|
70
78
|
* CallId
|
|
71
79
|
*
|
|
@@ -80,13 +88,6 @@ export interface CreateOutboundCallEventProperties {
|
|
|
80
88
|
* @type {TEXT}
|
|
81
89
|
*/
|
|
82
90
|
CallSid?: string | null;
|
|
83
|
-
/**
|
|
84
|
-
* Title
|
|
85
|
-
*
|
|
86
|
-
* Title of twilio call event record
|
|
87
|
-
* @type {TEXT}
|
|
88
|
-
*/
|
|
89
|
-
Title?: string | null;
|
|
90
91
|
}
|
|
91
92
|
/**
|
|
92
93
|
* CreateOutboundCallEvent
|
|
@@ -22,7 +22,7 @@ export interface UpdateCallEventAfterProcessingMessage extends OdinRecordUpdated
|
|
|
22
22
|
* Properties for UpdateCallEventAfterProcessing action
|
|
23
23
|
*
|
|
24
24
|
* @property Required fields: 2
|
|
25
|
-
* @property Optional fields:
|
|
25
|
+
* @property Optional fields: 0
|
|
26
26
|
*/
|
|
27
27
|
export interface UpdateCallEventAfterProcessingProperties {
|
|
28
28
|
/**
|
|
@@ -41,27 +41,6 @@ export interface UpdateCallEventAfterProcessingProperties {
|
|
|
41
41
|
* @required
|
|
42
42
|
*/
|
|
43
43
|
ProcessingStatus: string;
|
|
44
|
-
/**
|
|
45
|
-
* Title
|
|
46
|
-
*
|
|
47
|
-
* Title of twilio call event record
|
|
48
|
-
* @type {TEXT}
|
|
49
|
-
*/
|
|
50
|
-
Title?: string | null;
|
|
51
|
-
/**
|
|
52
|
-
* CallId
|
|
53
|
-
*
|
|
54
|
-
* Reference of the call record
|
|
55
|
-
* @type {LOOKUP}
|
|
56
|
-
*/
|
|
57
|
-
CallId?: string | null;
|
|
58
|
-
/**
|
|
59
|
-
* CallSid
|
|
60
|
-
*
|
|
61
|
-
* Twilio id of the call
|
|
62
|
-
* @type {TEXT}
|
|
63
|
-
*/
|
|
64
|
-
CallSid?: string | null;
|
|
65
44
|
}
|
|
66
45
|
/**
|
|
67
46
|
* UpdateCallEventAfterProcessing
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Process-model evaluation engine + frozen BP projections.
|
|
3
|
+
*
|
|
4
|
+
* data + process template + evaluation mechanism → correct / variance
|
|
5
|
+
*
|
|
6
|
+
* Deployable lift of the agent-os evaluation harness so backend services
|
|
7
|
+
* (e.g. the fulfillment-health API) can score live records against the BP
|
|
8
|
+
* source of truth server-side. Import via the full dist subpath:
|
|
9
|
+
* `@d19n/youfibre-odin-sdk/dist/process-model/index`
|
|
10
|
+
*/
|
|
11
|
+
export * from './types';
|
|
12
|
+
export * from './process-validator';
|
|
13
|
+
export { ORDER_PROCESS_PROJECTION } from './order-process.projection';
|
|
14
|
+
export { INSTALL_PROCESS_PROJECTION } from './install-process.projection';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.INSTALL_PROCESS_PROJECTION = exports.ORDER_PROCESS_PROJECTION = void 0;
|
|
18
|
+
/**
|
|
19
|
+
* Process-model evaluation engine + frozen BP projections.
|
|
20
|
+
*
|
|
21
|
+
* data + process template + evaluation mechanism → correct / variance
|
|
22
|
+
*
|
|
23
|
+
* Deployable lift of the agent-os evaluation harness so backend services
|
|
24
|
+
* (e.g. the fulfillment-health API) can score live records against the BP
|
|
25
|
+
* source of truth server-side. Import via the full dist subpath:
|
|
26
|
+
* `@d19n/youfibre-odin-sdk/dist/process-model/index`
|
|
27
|
+
*/
|
|
28
|
+
__exportStar(require("./types"), exports);
|
|
29
|
+
__exportStar(require("./process-validator"), exports);
|
|
30
|
+
var order_process_projection_1 = require("./order-process.projection");
|
|
31
|
+
Object.defineProperty(exports, "ORDER_PROCESS_PROJECTION", { enumerable: true, get: function () { return order_process_projection_1.ORDER_PROCESS_PROJECTION; } });
|
|
32
|
+
var install_process_projection_1 = require("./install-process.projection");
|
|
33
|
+
Object.defineProperty(exports, "INSTALL_PROCESS_PROJECTION", { enumerable: true, get: function () { return install_process_projection_1.INSTALL_PROCESS_PROJECTION; } });
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen evaluation projection of the INSTALL business process.
|
|
3
|
+
*
|
|
4
|
+
* Mechanical extraction of `agent-os/knowledge/business-processes/install.ts`
|
|
5
|
+
* — the EVALUATION-relevant fields only (stage key/name/terminal + thresholds
|
|
6
|
+
* + dependencies/onVariance). Rendering/BPMN fields (entryActions, stageJobs,
|
|
7
|
+
* handoffs) are intentionally omitted; the agent-os file remains the authoring
|
|
8
|
+
* source of truth. Same pattern as `wholesale-provider/install-stage-kci-spec.ts`.
|
|
9
|
+
*
|
|
10
|
+
* All INSTALL stages are listed (even those with no dependencies) so that a WO
|
|
11
|
+
* at any legitimate stage is RECOGNISED by the validator and scores `correct`,
|
|
12
|
+
* rather than falsely surfacing as `unmatched-variance` (stage-not-found).
|
|
13
|
+
*
|
|
14
|
+
* Verified against the live BP on 2026-05-23.
|
|
15
|
+
*/
|
|
16
|
+
import type { ProcessDef } from './types';
|
|
17
|
+
export declare const INSTALL_PROCESS_PROJECTION: ProcessDef;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.INSTALL_PROCESS_PROJECTION = void 0;
|
|
4
|
+
exports.INSTALL_PROCESS_PROJECTION = {
|
|
5
|
+
id: 'INSTALL',
|
|
6
|
+
title: 'Install Work Order',
|
|
7
|
+
entity: 'FieldServiceModule:WorkOrder',
|
|
8
|
+
scopeFilter: [{ column: 'Type', op: 'in', value: ['INSTALL'] }],
|
|
9
|
+
stages: [
|
|
10
|
+
{
|
|
11
|
+
key: 'WorkOrderStageAccepted',
|
|
12
|
+
name: 'Accepted',
|
|
13
|
+
warnAfter: '7d',
|
|
14
|
+
alertAfter: '14d',
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
key: 'WorkOrderStageScheduled',
|
|
18
|
+
name: 'Scheduled',
|
|
19
|
+
dependencies: [
|
|
20
|
+
{
|
|
21
|
+
kind: 'cross-system-record-exists',
|
|
22
|
+
id: 'install-scheduled-must-have-net-counterpart',
|
|
23
|
+
mapping: {
|
|
24
|
+
system: 'NET',
|
|
25
|
+
remoteEntity: 'FieldServiceModule:WorkOrder',
|
|
26
|
+
mapping: { fromProperty: 'id', toProperty: 'OdinExternalId' },
|
|
27
|
+
},
|
|
28
|
+
severity: 'BLOCKER',
|
|
29
|
+
notes: 'Every wholesale Scheduled WO must have a NET counterpart.',
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
kind: 'related-record',
|
|
33
|
+
id: 'install-scheduled-must-have-service-appointment',
|
|
34
|
+
relation: {
|
|
35
|
+
entity: 'FieldServiceModule:ServiceAppointment',
|
|
36
|
+
filter: [],
|
|
37
|
+
minCount: 1,
|
|
38
|
+
},
|
|
39
|
+
severity: 'WARN',
|
|
40
|
+
notes: 'Scheduled WO must have ≥1 linked ServiceAppointment.',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
{ key: 'WorkOrderStageEnRoute', name: 'En Route' },
|
|
45
|
+
{ key: 'WorkOrderStageInProgress', name: 'In Progress' },
|
|
46
|
+
{ key: 'WorkOrderRemediationRequired', name: 'Remediation Required' },
|
|
47
|
+
{
|
|
48
|
+
key: 'CustomerWorkOrdersStagePending',
|
|
49
|
+
name: 'Pending',
|
|
50
|
+
warnAfter: '3d',
|
|
51
|
+
alertAfter: '7d',
|
|
52
|
+
},
|
|
53
|
+
{ key: 'WorkOrderStageDone', name: 'Done', terminal: true },
|
|
54
|
+
{ key: 'WorkOrderStageCancelled', name: 'Cancelled', terminal: true },
|
|
55
|
+
],
|
|
56
|
+
};
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ORDER_PROCESS_PROJECTION = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Frozen evaluation projection of the ORDER business process.
|
|
6
|
+
*
|
|
7
|
+
* Mechanical extraction of `agent-os/knowledge/business-processes/order.ts`
|
|
8
|
+
* — EVALUATION-relevant fields only. v1 ports the OrderStageSupply dependency
|
|
9
|
+
* block (the fulfilment off-track surface: missing base product, NET
|
|
10
|
+
* dispatch/counterpart/commit gaps, the active-progressing-install fan-out,
|
|
11
|
+
* the OTS-progress gate, and the four INFO activation gates). Other stages are
|
|
12
|
+
* listed RECOGNISED but without ported dependencies for v1, so an Order at
|
|
13
|
+
* Sold/Active/Cancelled scores `correct` rather than `unmatched-variance`.
|
|
14
|
+
* Extend per-stage as later passes need them.
|
|
15
|
+
*
|
|
16
|
+
* Activation-gate invariants carry `predicateRef: order.activationGate.*`,
|
|
17
|
+
* which the lifted engine does NOT register (severity INFO) → they surface as
|
|
18
|
+
* `unsupported-kind` (honest coverage gap), never as a data variance.
|
|
19
|
+
*
|
|
20
|
+
* Verified against the live BP on 2026-05-23.
|
|
21
|
+
*/
|
|
22
|
+
const ChangeReason_1 = require("../entities-v2/ChangeReason");
|
|
23
|
+
exports.ORDER_PROCESS_PROJECTION = {
|
|
24
|
+
id: 'ORDER',
|
|
25
|
+
title: 'Order',
|
|
26
|
+
entity: 'OrderModule:Order',
|
|
27
|
+
stages: [
|
|
28
|
+
{ key: 'OrderStageDraft', name: 'Draft' },
|
|
29
|
+
{ key: 'OrderStagePreOrder', name: 'Pre-Order' },
|
|
30
|
+
{ key: 'OrderStageSold', name: 'Sold' },
|
|
31
|
+
{
|
|
32
|
+
key: 'OrderStageSupply',
|
|
33
|
+
name: 'Supply',
|
|
34
|
+
warnAfter: '15d',
|
|
35
|
+
alertAfter: '30d',
|
|
36
|
+
dependencies: [
|
|
37
|
+
{
|
|
38
|
+
kind: 'related-record',
|
|
39
|
+
id: 'supply-must-have-base-product-item',
|
|
40
|
+
relation: {
|
|
41
|
+
entity: 'OrderModule:OrderItem',
|
|
42
|
+
filter: [{ column: 'ProductType', op: 'eq', value: 'BASE_PRODUCT' }],
|
|
43
|
+
minCount: 1,
|
|
44
|
+
},
|
|
45
|
+
severity: 'BLOCKER',
|
|
46
|
+
notes: 'A Supply order must carry the BASE_PRODUCT order item. Missing = offer misconfiguration that blocks NET dispatch and activation. Revert to Draft for correction.',
|
|
47
|
+
onVariance: [
|
|
48
|
+
{
|
|
49
|
+
when: 'no-related-records',
|
|
50
|
+
action: {
|
|
51
|
+
kind: 'revert-parent-to',
|
|
52
|
+
stageKey: 'OrderStageDraft',
|
|
53
|
+
notes: 'Supply order has zero OrderItem links — checkout defect. Revert to Draft for investigation.',
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
when: 'no-records-matching',
|
|
58
|
+
match: [{ column: 'ProductType', op: 'eq', value: 'BASE_PRODUCT' }],
|
|
59
|
+
action: {
|
|
60
|
+
kind: 'revert-parent-to',
|
|
61
|
+
stageKey: 'OrderStageDraft',
|
|
62
|
+
notes: 'Supply order has items but no BASE_PRODUCT. Offer misconfiguration — cannot dispatch to NET. Revert to Draft for correction.',
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
kind: 'process-success',
|
|
69
|
+
id: 'supply-must-have-net-create-provide-success',
|
|
70
|
+
process: 'NetomniaCreateProvideNewOrder',
|
|
71
|
+
matchOn: 'journeyId',
|
|
72
|
+
withinWindow: '90d',
|
|
73
|
+
severity: 'BLOCKER',
|
|
74
|
+
notes: 'FNO outbound must succeed for NETOMNIA-wholesale providers.',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
kind: 'cross-system-record-exists',
|
|
78
|
+
id: 'supply-must-have-net-counterpart',
|
|
79
|
+
parentFilter: [
|
|
80
|
+
{ column: 'Provider', op: 'in', value: ['BRSK', 'NETOMNIA', 'NETOMNIA_LEGACY'] },
|
|
81
|
+
],
|
|
82
|
+
mapping: {
|
|
83
|
+
system: 'NET',
|
|
84
|
+
remoteEntity: 'OrderModule:Order',
|
|
85
|
+
mapping: { fromProperty: 'id', toProperty: 'ExternalRef' },
|
|
86
|
+
},
|
|
87
|
+
severity: 'BLOCKER',
|
|
88
|
+
notes: 'Every Supply order on a NET-platform provider must have a NET counterpart by ExternalRef. Missing counterpart = YF→NET PROVIDE dispatch never landed (or NET-side dropped the record).',
|
|
89
|
+
onVariance: [
|
|
90
|
+
{
|
|
91
|
+
when: 'no-cross-system-record',
|
|
92
|
+
action: {
|
|
93
|
+
kind: 'manual-triage',
|
|
94
|
+
reason: 'Operator intent: re-fire YF→NET PROVIDE dispatch on the parent order. Idempotency-safe in YF (createNetomniaOrder probes for existing NET counterpart and short-circuits with IDEMPOTENT_SKIP_ALREADY_IN_NET when found). The YF endpoint exists at OrderModule/v1.0/netomnia/create-provide-order.',
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
kind: 'related-record',
|
|
101
|
+
id: 'supply-must-have-committed-kci',
|
|
102
|
+
parentFilter: [
|
|
103
|
+
{ column: 'Provider', op: 'in', value: ['BRSK', 'NETOMNIA', 'NETOMNIA_LEGACY'] },
|
|
104
|
+
],
|
|
105
|
+
relation: {
|
|
106
|
+
entity: 'OrderModule:OrderKci',
|
|
107
|
+
filter: [{ column: 'Type', op: 'in', value: ['COMMITTED', 'ACKNOWLEDGED'] }],
|
|
108
|
+
minCount: 1,
|
|
109
|
+
},
|
|
110
|
+
severity: 'BLOCKER',
|
|
111
|
+
notes: 'Supply means YF dispatched AND provider confirmed (COMMITTED or ACKNOWLEDGED OrderKci). Variance = NET dispatched but provider has not confirmed — chase the provider or re-dispatch via /retry-sold-to-supply.',
|
|
112
|
+
onVariance: [
|
|
113
|
+
{
|
|
114
|
+
when: 'no-related-records',
|
|
115
|
+
action: {
|
|
116
|
+
kind: 'manual-triage',
|
|
117
|
+
reason: 'No OrderKci of any kind on this Supply order — NET dispatch may have landed but no KCI received. Investigate whether the NET counterpart was actually created.',
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
when: 'no-records-matching',
|
|
122
|
+
match: [{ column: 'Type', op: 'in', value: ['COMMITTED', 'ACKNOWLEDGED'] }],
|
|
123
|
+
action: {
|
|
124
|
+
kind: 'manual-triage',
|
|
125
|
+
reason: 'OrderKcis exist but none are COMMITTED/ACKNOWLEDGED — provider received the order but has not committed. Normal for very-recent Supply orders (<24h); persistent variance = provider-side hold, chase or re-dispatch.',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
],
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
kind: 'related-record',
|
|
132
|
+
id: 'supply-must-have-active-progressing-install',
|
|
133
|
+
relation: {
|
|
134
|
+
entity: 'FieldServiceModule:WorkOrder',
|
|
135
|
+
filter: [
|
|
136
|
+
{ column: 'Type', op: 'in', value: ['INSTALL', 'INSTALL_BUSINESS'] },
|
|
137
|
+
{
|
|
138
|
+
column: 'StageKey',
|
|
139
|
+
op: 'in',
|
|
140
|
+
value: [
|
|
141
|
+
// INSTALL active-progressing (5 — drops Accepted)
|
|
142
|
+
'WorkOrderStageScheduled',
|
|
143
|
+
'WorkOrderStageEnRoute',
|
|
144
|
+
'WorkOrderStageInProgress',
|
|
145
|
+
'WorkOrderRemediationRequired',
|
|
146
|
+
'CustomerWorkOrdersStagePending',
|
|
147
|
+
// INSTALL_BUSINESS active-progressing (7)
|
|
148
|
+
'BusinessInstallPipelineStageDesktopSurveyRequired',
|
|
149
|
+
'BusinessInstallPipelineStageReadyForInstallation',
|
|
150
|
+
'BusinessInstallPipelineStageScheduled',
|
|
151
|
+
'BusinessInstallPipelineStageEnRoute',
|
|
152
|
+
'BusinessInstallPipelineStageSiteSurveyRequired',
|
|
153
|
+
'BusinessInstallPipelineStageRemediationRequired',
|
|
154
|
+
'BusinessInstallPipelineStageInProgress',
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
minCount: 1,
|
|
159
|
+
},
|
|
160
|
+
severity: 'BLOCKER',
|
|
161
|
+
notes: 'Supply must have ≥1 actively-progressing INSTALL/INSTALL_BUSINESS WO. Variance fans out to multiple remediations by shape — see onVariance.',
|
|
162
|
+
onVariance: [
|
|
163
|
+
{
|
|
164
|
+
when: 'no-related-records',
|
|
165
|
+
action: {
|
|
166
|
+
kind: 'revert-parent-to',
|
|
167
|
+
stageKey: 'OrderStageSold',
|
|
168
|
+
notes: 'No WorkOrders linked at all. Order needs to re-flow through fulfilment.',
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
when: 'no-records-matching',
|
|
173
|
+
match: [{ column: 'Type', op: 'in', value: ['INSTALL', 'INSTALL_BUSINESS'] }],
|
|
174
|
+
action: {
|
|
175
|
+
kind: 'revert-parent-to',
|
|
176
|
+
stageKey: 'OrderStageSold',
|
|
177
|
+
notes: 'Linked WOs exist (e.g. SERVICE) but no INSTALL/INSTALL_BUSINESS was ever created. Revert so fulfilment dispatch retries from Sold.',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
when: 'all-records-matching-also-match',
|
|
182
|
+
selector: [{ column: 'Type', op: 'in', value: ['INSTALL', 'INSTALL_BUSINESS'] }],
|
|
183
|
+
additional: [{ column: 'StageKey', op: 'in', value: ['WorkOrderStageAccepted'] }],
|
|
184
|
+
action: {
|
|
185
|
+
kind: 'revert-parent-to',
|
|
186
|
+
stageKey: 'OrderStageSold',
|
|
187
|
+
notes: 'Every INSTALL is in Accepted — supplier acknowledged but never booked an appointment. Revert so a fresh Sold→Supply pass can re-dispatch.',
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
when: 'all-records-matching-also-match',
|
|
192
|
+
selector: [{ column: 'Type', op: 'in', value: ['INSTALL', 'INSTALL_BUSINESS'] }],
|
|
193
|
+
additional: [
|
|
194
|
+
{
|
|
195
|
+
column: 'StageKey',
|
|
196
|
+
op: 'in',
|
|
197
|
+
value: ['WorkOrderStageCancelled', 'BusinessInstallPipelineStageCancelled'],
|
|
198
|
+
},
|
|
199
|
+
],
|
|
200
|
+
action: {
|
|
201
|
+
kind: 'cancel-parent',
|
|
202
|
+
stageKey: 'OrderStageCancelled',
|
|
203
|
+
properties: {
|
|
204
|
+
CancellationReason: ChangeReason_1.ChangeReasonCancelReason.CANCELLATION_FOR_PERSONAL_REASONS,
|
|
205
|
+
},
|
|
206
|
+
notes: 'Every INSTALL/INSTALL_BUSINESS is Cancelled with no replacement. Cancel cleanly so cancellation history is preserved.',
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
when: 'all-records-matching-also-match',
|
|
211
|
+
selector: [{ column: 'Type', op: 'in', value: ['INSTALL', 'INSTALL_BUSINESS'] }],
|
|
212
|
+
additional: [
|
|
213
|
+
{
|
|
214
|
+
column: 'StageKey',
|
|
215
|
+
op: 'in',
|
|
216
|
+
value: ['WorkOrderStageDone', 'BusinessInstallPipelineStageComplete'],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
action: {
|
|
220
|
+
kind: 'invoke-platform-api',
|
|
221
|
+
endpoint: '/OrderModule/v1.0/orders/{parent.id}/retry-supply-to-active',
|
|
222
|
+
method: 'post',
|
|
223
|
+
notes: 'Every INSTALL/INSTALL_BUSINESS WO is Done/Complete — engineer finished but Order is stuck in Supply. Trigger the Supply→Active eval; the endpoint handles billing eligibility + ONT activation gates.',
|
|
224
|
+
},
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
kind: 'related-record',
|
|
230
|
+
id: 'supply-gaining-ots-must-progress',
|
|
231
|
+
relation: {
|
|
232
|
+
entity: 'OrderModule:OneTouchSwitch',
|
|
233
|
+
filter: [
|
|
234
|
+
{ column: 'EntityType', op: 'eq', value: 'GAINING' },
|
|
235
|
+
{ column: 'Status', op: 'eq', value: 'SWITCH_MATCH_REQUEST_CONFIRMED' },
|
|
236
|
+
],
|
|
237
|
+
maxCount: 0,
|
|
238
|
+
},
|
|
239
|
+
severity: 'BLOCKER',
|
|
240
|
+
notes: 'Order in Supply with linked GAINING OTS must NOT have OTS stuck at SWITCH_MATCH_REQUEST_CONFIRMED past the 24h SLO.',
|
|
241
|
+
onVariance: [
|
|
242
|
+
{
|
|
243
|
+
when: 'otherwise',
|
|
244
|
+
action: {
|
|
245
|
+
kind: 'manual-triage',
|
|
246
|
+
reason: 'Operator intent: fire residentialSwitchOrderRequest against the GAINING OTS stuck at SWITCH_MATCH_REQUEST_CONFIRMED, deriving PlannedSwitchDate from the linked Order → INSTALL WO → ServiceAppointment.Date.',
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
},
|
|
251
|
+
// Activation gates (INFO) — predicateRef not registered in the lifted
|
|
252
|
+
// engine → surface as unsupported-kind, never a variance.
|
|
253
|
+
{
|
|
254
|
+
kind: 'invariant',
|
|
255
|
+
id: 'supply-activation-gate-install-success',
|
|
256
|
+
description: 'Order has ≥1 linked INSTALL / INSTALL_BUSINESS / SERVICE WorkOrder at a success stage (Done / Complete) — the load-bearing causal trigger for activation evaluation.',
|
|
257
|
+
severity: 'INFO',
|
|
258
|
+
predicateRef: 'order.activationGate.installSuccess',
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
kind: 'invariant',
|
|
262
|
+
id: 'supply-activation-gate-base-product',
|
|
263
|
+
description: 'Order has items, including ≥1 BASE_PRODUCT order item. Without a base product, Active has no recurring revenue line to bill.',
|
|
264
|
+
severity: 'INFO',
|
|
265
|
+
predicateRef: 'order.activationGate.baseProduct',
|
|
266
|
+
},
|
|
267
|
+
{
|
|
268
|
+
kind: 'invariant',
|
|
269
|
+
id: 'supply-activation-gate-ots-trigger-confirmed',
|
|
270
|
+
description: 'For OTS GAINING orders only: linked OneTouchSwitch Status must be SWITCH_ORDER_TRIGGER_CONFIRMED (transitionSupplyToActive fires switch-trigger-request inline if at REQUEST/UPDATE_CONFIRMED).',
|
|
271
|
+
severity: 'INFO',
|
|
272
|
+
predicateRef: 'order.activationGate.otsTriggerConfirmed',
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
kind: 'invariant',
|
|
276
|
+
id: 'supply-activation-gate-ont-health',
|
|
277
|
+
description: 'ONT health on the linked Premise: AdminStatus=up AND OperStatus=up. CITYFIBRE bypassed.',
|
|
278
|
+
severity: 'INFO',
|
|
279
|
+
predicateRef: 'order.activationGate.ontHealthUp',
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
},
|
|
283
|
+
{ key: 'OrderStageActive', name: 'Active' },
|
|
284
|
+
{ key: 'OrderStageDeactivated', name: 'Deactivated' },
|
|
285
|
+
{ key: 'OrderStagePendingCancellation', name: 'Pending Cancellation' },
|
|
286
|
+
{ key: 'OrderStageCancelled', name: 'Cancelled' },
|
|
287
|
+
{ key: 'OrderStageBrskMigrationRemediation', name: 'BRSK Migration Remediation' },
|
|
288
|
+
],
|
|
289
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic process-template evaluation engine (deployable lift).
|
|
3
|
+
*
|
|
4
|
+
* data + process template + evaluation mechanism → correct / variance
|
|
5
|
+
*
|
|
6
|
+
* Lifted verbatim from `agent-os/tools/ops/src/lib/process-validator.ts` so
|
|
7
|
+
* the deployed fulfillment-health API can score live records against the
|
|
8
|
+
* frozen `ProcessDef` projections server-side. Pure function — no SDK calls,
|
|
9
|
+
* no mutation, no I/O. The caller pre-hydrates relations / cross-system
|
|
10
|
+
* records / process-success indexes.
|
|
11
|
+
*
|
|
12
|
+
* Design rules (project memory `project_data_template_evaluation_model.md`):
|
|
13
|
+
* - The engine evaluates schema; it does not encode business decisions.
|
|
14
|
+
* - A failing dependency with no matching `onVariance` clause → `unmatched-
|
|
15
|
+
* variance` (never a silent default). This is the signal the template is
|
|
16
|
+
* incomplete — the BP-gap-analysis trigger.
|
|
17
|
+
* - A dependency whose `kind` the caller didn't hydrate → `unsupported-kind`,
|
|
18
|
+
* distinguishing "coverage gap this run" from "template extension needed".
|
|
19
|
+
*/
|
|
20
|
+
import type { ActionDecl, Dependency, ProcessDef, ProcessSuccessRecord } from './types';
|
|
21
|
+
/**
|
|
22
|
+
* Minimal record shape the engine needs. Concrete SDK record types
|
|
23
|
+
* (OrderRecord, WorkOrderRecord, etc.) structurally satisfy this.
|
|
24
|
+
*/
|
|
25
|
+
export interface MinimalRecord {
|
|
26
|
+
id?: string;
|
|
27
|
+
type?: string;
|
|
28
|
+
recordNumber?: string;
|
|
29
|
+
stage?: {
|
|
30
|
+
key?: string;
|
|
31
|
+
name?: string;
|
|
32
|
+
};
|
|
33
|
+
properties?: Record<string, unknown>;
|
|
34
|
+
createdAt?: string;
|
|
35
|
+
updatedAt?: string;
|
|
36
|
+
}
|
|
37
|
+
export interface ValidationContext {
|
|
38
|
+
record: MinimalRecord;
|
|
39
|
+
process: ProcessDef;
|
|
40
|
+
/** Hydrated related records, keyed by canonical Odin entity key. */
|
|
41
|
+
relations: Record<string, MinimalRecord[]>;
|
|
42
|
+
/** Hydrated cross-system records, keyed by `<system>:<remoteEntity>`. */
|
|
43
|
+
crossSystemRecords?: Record<string, MinimalRecord[]>;
|
|
44
|
+
/** process-success indexes keyed by `${process}::${withinWindow ?? '<no-window>'}`. */
|
|
45
|
+
processSuccessIndexes?: Record<string, Map<string, ProcessSuccessRecord>>;
|
|
46
|
+
}
|
|
47
|
+
export declare type DepStatus = 'correct' | 'variance' | 'unmatched-variance' | 'unsupported-kind';
|
|
48
|
+
export interface DepVerdict {
|
|
49
|
+
depId: string;
|
|
50
|
+
depKind: Dependency['kind'];
|
|
51
|
+
status: DepStatus;
|
|
52
|
+
varianceShape?: string;
|
|
53
|
+
varianceActualSummary?: string;
|
|
54
|
+
prescribedAction?: ActionDecl;
|
|
55
|
+
unresolvedReason?: string;
|
|
56
|
+
}
|
|
57
|
+
export declare type AgeStatus = 'green' | 'warn' | 'alert' | 'unknown';
|
|
58
|
+
export interface AgeInStage {
|
|
59
|
+
days: number;
|
|
60
|
+
status: AgeStatus;
|
|
61
|
+
warnAfterDays?: number;
|
|
62
|
+
alertAfterDays?: number;
|
|
63
|
+
}
|
|
64
|
+
export interface ValidationReport {
|
|
65
|
+
recordId: string;
|
|
66
|
+
recordNumber?: string;
|
|
67
|
+
stageKey: string;
|
|
68
|
+
stageRecognised: boolean;
|
|
69
|
+
verdicts: DepVerdict[];
|
|
70
|
+
ageInStage: AgeInStage;
|
|
71
|
+
/** Priority: 'variance' > 'unmatched-variance' > 'correct' > 'unsupported-kind'. */
|
|
72
|
+
overall: DepStatus;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Evaluate `ctx.record` against `ctx.process` for the record's current stage.
|
|
76
|
+
*/
|
|
77
|
+
export declare function validateAgainstProcess(ctx: ValidationContext): ValidationReport;
|
|
78
|
+
export declare function buildProcessSuccessIndexKey(process: string, withinWindow: string | undefined): string;
|