@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.
@@ -0,0 +1,509 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildProcessSuccessIndexKey = exports.validateAgainstProcess = void 0;
4
+ /**
5
+ * Evaluate `ctx.record` against `ctx.process` for the record's current stage.
6
+ */
7
+ function validateAgainstProcess(ctx) {
8
+ var _a, _b, _c, _d, _e;
9
+ const stageKey = (_b = (_a = ctx.record.stage) === null || _a === void 0 ? void 0 : _a.key) !== null && _b !== void 0 ? _b : '';
10
+ const stage = ctx.process.stages.find((s) => s.key === stageKey);
11
+ if (!stage) {
12
+ return {
13
+ recordId: (_c = ctx.record.id) !== null && _c !== void 0 ? _c : '<unknown>',
14
+ recordNumber: ctx.record.recordNumber,
15
+ stageKey,
16
+ stageRecognised: false,
17
+ verdicts: [],
18
+ ageInStage: computeAgeInStage(ctx.record, undefined, undefined),
19
+ overall: 'unmatched-variance',
20
+ };
21
+ }
22
+ const verdicts = ((_d = stage.dependencies) !== null && _d !== void 0 ? _d : []).map((dep) => evaluateDependency(dep, ctx));
23
+ return {
24
+ recordId: (_e = ctx.record.id) !== null && _e !== void 0 ? _e : '<unknown>',
25
+ recordNumber: ctx.record.recordNumber,
26
+ stageKey,
27
+ stageRecognised: true,
28
+ verdicts,
29
+ ageInStage: computeAgeInStage(ctx.record, stage.warnAfter, stage.alertAfter),
30
+ overall: computeOverall(verdicts),
31
+ };
32
+ }
33
+ exports.validateAgainstProcess = validateAgainstProcess;
34
+ // ──────────────────────────────────────────────────────────────────────
35
+ // Aging
36
+ // ──────────────────────────────────────────────────────────────────────
37
+ function parseDurationToDays(s) {
38
+ if (!s)
39
+ return undefined;
40
+ const match = /^(\d+)\s*([dhmw])$/.exec(s.trim());
41
+ if (!match)
42
+ return undefined;
43
+ const n = Number.parseInt(match[1], 10);
44
+ switch (match[2]) {
45
+ case 'h':
46
+ return n / 24;
47
+ case 'd':
48
+ return n;
49
+ case 'w':
50
+ return n * 7;
51
+ case 'm':
52
+ return n * 30;
53
+ default:
54
+ return undefined;
55
+ }
56
+ }
57
+ function computeAgeInStage(record, warnAfter, alertAfter) {
58
+ if (!record.createdAt) {
59
+ return { days: -1, status: 'unknown' };
60
+ }
61
+ const createdMs = new Date(record.createdAt).getTime();
62
+ if (Number.isNaN(createdMs)) {
63
+ return { days: -1, status: 'unknown' };
64
+ }
65
+ const days = Math.floor((Date.now() - createdMs) / (24 * 60 * 60 * 1000));
66
+ const warnAfterDays = parseDurationToDays(warnAfter);
67
+ const alertAfterDays = parseDurationToDays(alertAfter);
68
+ let status = 'green';
69
+ if (alertAfterDays !== undefined && days >= alertAfterDays)
70
+ status = 'alert';
71
+ else if (warnAfterDays !== undefined && days >= warnAfterDays)
72
+ status = 'warn';
73
+ return { days, status, warnAfterDays, alertAfterDays };
74
+ }
75
+ // ──────────────────────────────────────────────────────────────────────
76
+ // Dependency dispatch
77
+ // ──────────────────────────────────────────────────────────────────────
78
+ function evaluateDependency(dep, ctx) {
79
+ switch (dep.kind) {
80
+ case 'related-record':
81
+ return evaluateRelatedRecord(dep, ctx);
82
+ case 'cross-system-record-exists':
83
+ return evaluateCrossSystemRecordExists(dep, ctx);
84
+ case 'process-success':
85
+ return evaluateProcessSuccess(dep, ctx);
86
+ case 'invariant':
87
+ return evaluateInvariant(dep, ctx);
88
+ }
89
+ }
90
+ const INVARIANT_PREDICATES = {
91
+ 'stage-age-lte': (record, arg) => {
92
+ if (!arg)
93
+ return 'unknown';
94
+ const days = parseDurationToDays(arg);
95
+ if (days === undefined)
96
+ return 'unknown';
97
+ if (!record.createdAt)
98
+ return 'unknown';
99
+ const createdMs = new Date(record.createdAt).getTime();
100
+ if (Number.isNaN(createdMs))
101
+ return 'unknown';
102
+ const ageDays = (Date.now() - createdMs) / (24 * 60 * 60 * 1000);
103
+ return ageDays <= days;
104
+ },
105
+ };
106
+ function evaluateInvariant(dep, ctx) {
107
+ var _a;
108
+ const ref = dep.predicateRef;
109
+ if (!ref) {
110
+ return {
111
+ depId: dep.id,
112
+ depKind: 'invariant',
113
+ status: 'unsupported-kind',
114
+ unresolvedReason: `Invariant dep '${dep.id}' has no predicateRef declared; engine cannot evaluate.`,
115
+ };
116
+ }
117
+ const [predicateId, ...rest] = ref.split(':');
118
+ const arg = rest.length > 0 ? rest.join(':') : undefined;
119
+ const predicate = INVARIANT_PREDICATES[predicateId];
120
+ if (!predicate) {
121
+ const action = findInvariantVarianceMatch(dep, 'predicate-not-registered');
122
+ if (action) {
123
+ return {
124
+ depId: dep.id,
125
+ depKind: 'invariant',
126
+ status: 'variance',
127
+ varianceShape: 'predicate-not-registered',
128
+ varianceActualSummary: `predicateRef '${ref}' is not registered in the engine`,
129
+ prescribedAction: action,
130
+ };
131
+ }
132
+ return {
133
+ depId: dep.id,
134
+ depKind: 'invariant',
135
+ status: 'unsupported-kind',
136
+ unresolvedReason: `Predicate '${predicateId}' (from predicateRef '${ref}') is not registered in INVARIANT_PREDICATES.`,
137
+ };
138
+ }
139
+ const result = predicate(ctx.record, arg);
140
+ if (result === 'unknown') {
141
+ return {
142
+ depId: dep.id,
143
+ depKind: 'invariant',
144
+ status: 'unsupported-kind',
145
+ unresolvedReason: `Predicate '${predicateId}' returned 'unknown' for record ${(_a = ctx.record.id) !== null && _a !== void 0 ? _a : '<no-id>'} — a required input is missing.`,
146
+ };
147
+ }
148
+ if (result === true) {
149
+ return { depId: dep.id, depKind: 'invariant', status: 'correct' };
150
+ }
151
+ const action = findInvariantVarianceMatch(dep, 'predicate-failed');
152
+ if (!action) {
153
+ return {
154
+ depId: dep.id,
155
+ depKind: 'invariant',
156
+ status: 'unmatched-variance',
157
+ varianceActualSummary: `predicate '${ref}' returned false`,
158
+ unresolvedReason: `Predicate '${ref}' failed and no onVariance clause matched 'predicate-failed'.`,
159
+ };
160
+ }
161
+ return {
162
+ depId: dep.id,
163
+ depKind: 'invariant',
164
+ status: 'variance',
165
+ varianceShape: 'predicate-failed',
166
+ varianceActualSummary: `predicate '${ref}' returned false`,
167
+ prescribedAction: action,
168
+ };
169
+ }
170
+ function findInvariantVarianceMatch(dep, shape) {
171
+ if (!dep.onVariance || dep.onVariance.length === 0)
172
+ return null;
173
+ for (const clause of dep.onVariance) {
174
+ if (clause.when === shape)
175
+ return clause.action;
176
+ }
177
+ for (const clause of dep.onVariance) {
178
+ if (clause.when === 'otherwise')
179
+ return clause.action;
180
+ }
181
+ return null;
182
+ }
183
+ // ──────────────────────────────────────────────────────────────────────
184
+ // related-record kind
185
+ // ──────────────────────────────────────────────────────────────────────
186
+ function evaluateRelatedRecord(dep, ctx) {
187
+ var _a, _b;
188
+ if (dep.parentFilter && !matchesAllFilters(ctx.record, dep.parentFilter)) {
189
+ return { depId: getDepId(dep), depKind: 'related-record', status: 'correct' };
190
+ }
191
+ const records = (_a = ctx.relations[dep.relation.entity]) !== null && _a !== void 0 ? _a : [];
192
+ const matching = records.filter((r) => matchesAllFilters(r, dep.relation.filter));
193
+ const minCount = (_b = dep.relation.minCount) !== null && _b !== void 0 ? _b : (dep.relation.maxCount !== undefined ? 0 : 1);
194
+ const maxCount = dep.relation.maxCount;
195
+ const meetsMin = matching.length >= minCount;
196
+ const meetsMax = maxCount === undefined || matching.length <= maxCount;
197
+ if (meetsMin && meetsMax) {
198
+ return { depId: getDepId(dep), depKind: 'related-record', status: 'correct' };
199
+ }
200
+ const clauseMatch = findRelatedRecordVarianceMatch(dep, records);
201
+ if (!clauseMatch) {
202
+ return {
203
+ depId: getDepId(dep),
204
+ depKind: 'related-record',
205
+ status: 'unmatched-variance',
206
+ varianceActualSummary: summariseRecords(records, dep.relation.entity),
207
+ unresolvedReason: `Filter not satisfied (matching=${matching.length}, minCount=${minCount}${maxCount === undefined ? '' : `, maxCount=${maxCount}`}) and no onVariance clause matched the actual data shape.`,
208
+ };
209
+ }
210
+ return {
211
+ depId: getDepId(dep),
212
+ depKind: 'related-record',
213
+ status: 'variance',
214
+ varianceShape: clauseMatch.shape,
215
+ varianceActualSummary: clauseMatch.summary,
216
+ prescribedAction: clauseMatch.action,
217
+ };
218
+ }
219
+ function findRelatedRecordVarianceMatch(dep, records) {
220
+ if (!dep.onVariance || dep.onVariance.length === 0)
221
+ return null;
222
+ for (const clause of dep.onVariance) {
223
+ switch (clause.when) {
224
+ case 'no-related-records': {
225
+ if (records.length === 0) {
226
+ return {
227
+ shape: 'no-related-records',
228
+ summary: 'no related records linked at all',
229
+ action: clause.action,
230
+ };
231
+ }
232
+ break;
233
+ }
234
+ case 'no-records-matching': {
235
+ const anyMatch = records.some((r) => matchesAllFilters(r, clause.match));
236
+ if (!anyMatch) {
237
+ return {
238
+ shape: 'no-records-matching',
239
+ summary: `${records.length} record(s) linked, none match: ${describeFilters(clause.match)}`,
240
+ action: clause.action,
241
+ };
242
+ }
243
+ break;
244
+ }
245
+ case 'all-records-matching-also-match': {
246
+ const selectorMatches = records.filter((r) => matchesAllFilters(r, clause.selector));
247
+ if (selectorMatches.length === 0)
248
+ break;
249
+ const allAlsoMatch = selectorMatches.every((r) => matchesAllFilters(r, clause.additional));
250
+ if (allAlsoMatch) {
251
+ return {
252
+ shape: 'all-records-matching-also-match',
253
+ summary: `${selectorMatches.length} record(s) match selector ${describeFilters(clause.selector)}; all also match ${describeFilters(clause.additional)}`,
254
+ action: clause.action,
255
+ };
256
+ }
257
+ break;
258
+ }
259
+ case 'otherwise': {
260
+ return {
261
+ shape: 'otherwise',
262
+ summary: `${records.length} record(s) linked; no specific clause matched, falling through to otherwise`,
263
+ action: clause.action,
264
+ };
265
+ }
266
+ }
267
+ }
268
+ return null;
269
+ }
270
+ // ──────────────────────────────────────────────────────────────────────
271
+ // cross-system-record-exists kind
272
+ // ──────────────────────────────────────────────────────────────────────
273
+ function evaluateCrossSystemRecordExists(dep, ctx) {
274
+ var _a, _b, _c, _d;
275
+ if (dep.parentFilter && !matchesAllFilters(ctx.record, dep.parentFilter)) {
276
+ return {
277
+ depId: getDepId(dep),
278
+ depKind: 'cross-system-record-exists',
279
+ status: 'correct',
280
+ };
281
+ }
282
+ const key = `${dep.mapping.system}:${dep.mapping.remoteEntity}`;
283
+ const records = (_a = ctx.crossSystemRecords) === null || _a === void 0 ? void 0 : _a[key];
284
+ if (records === undefined) {
285
+ return {
286
+ depId: getDepId(dep),
287
+ depKind: 'cross-system-record-exists',
288
+ status: 'unsupported-kind',
289
+ unresolvedReason: `Cross-system records for '${key}' were not hydrated for this run; engine cannot evaluate this dep.`,
290
+ };
291
+ }
292
+ if (records.length === 0) {
293
+ const clauseAction = findCrossSystemVarianceMatch(dep, 'no-cross-system-record');
294
+ if (!clauseAction) {
295
+ return {
296
+ depId: getDepId(dep),
297
+ depKind: 'cross-system-record-exists',
298
+ status: 'unmatched-variance',
299
+ varianceActualSummary: `no ${key} record found`,
300
+ unresolvedReason: `Cross-system counterpart on ${dep.mapping.system} not found and no onVariance clause matched 'no-cross-system-record'.`,
301
+ };
302
+ }
303
+ return {
304
+ depId: getDepId(dep),
305
+ depKind: 'cross-system-record-exists',
306
+ status: 'variance',
307
+ varianceShape: 'no-cross-system-record',
308
+ varianceActualSummary: `no ${key} record found by ${dep.mapping.mapping.fromProperty}→${dep.mapping.mapping.toProperty}`,
309
+ prescribedAction: clauseAction,
310
+ };
311
+ }
312
+ if (dep.remoteFilter && dep.remoteFilter.length > 0) {
313
+ const matching = records.filter((r) => { var _a; return matchesAllFilters(r, (_a = dep.remoteFilter) !== null && _a !== void 0 ? _a : []); });
314
+ if (matching.length === 0) {
315
+ const clauseAction = findCrossSystemVarianceMatch(dep, 'cross-system-record-mismatched');
316
+ const summary = `${records.length} ${key} record(s) found; none match ${describeFilters(dep.remoteFilter)}; sample StageKey=${(_d = (_c = (_b = records[0]) === null || _b === void 0 ? void 0 : _b.stage) === null || _c === void 0 ? void 0 : _c.key) !== null && _d !== void 0 ? _d : '<none>'}`;
317
+ if (!clauseAction) {
318
+ return {
319
+ depId: getDepId(dep),
320
+ depKind: 'cross-system-record-exists',
321
+ status: 'unmatched-variance',
322
+ varianceActualSummary: summary,
323
+ unresolvedReason: `Cross-system counterpart found but doesn't match remoteFilter, and no onVariance clause matched 'cross-system-record-mismatched'.`,
324
+ };
325
+ }
326
+ return {
327
+ depId: getDepId(dep),
328
+ depKind: 'cross-system-record-exists',
329
+ status: 'variance',
330
+ varianceShape: 'cross-system-record-mismatched',
331
+ varianceActualSummary: summary,
332
+ prescribedAction: clauseAction,
333
+ };
334
+ }
335
+ }
336
+ return {
337
+ depId: getDepId(dep),
338
+ depKind: 'cross-system-record-exists',
339
+ status: 'correct',
340
+ };
341
+ }
342
+ function findCrossSystemVarianceMatch(dep, shape) {
343
+ if (!dep.onVariance || dep.onVariance.length === 0)
344
+ return null;
345
+ for (const clause of dep.onVariance) {
346
+ if (clause.when === shape)
347
+ return clause.action;
348
+ }
349
+ for (const clause of dep.onVariance) {
350
+ if (clause.when === 'otherwise')
351
+ return clause.action;
352
+ }
353
+ return null;
354
+ }
355
+ // ──────────────────────────────────────────────────────────────────────
356
+ // process-success kind
357
+ // ──────────────────────────────────────────────────────────────────────
358
+ function buildProcessSuccessIndexKey(process, withinWindow) {
359
+ return `${process}::${withinWindow !== null && withinWindow !== void 0 ? withinWindow : '<no-window>'}`;
360
+ }
361
+ exports.buildProcessSuccessIndexKey = buildProcessSuccessIndexKey;
362
+ function evaluateProcessSuccess(dep, ctx) {
363
+ var _a, _b, _c, _d;
364
+ const indexKey = buildProcessSuccessIndexKey(dep.process, dep.withinWindow);
365
+ const index = (_a = ctx.processSuccessIndexes) === null || _a === void 0 ? void 0 : _a[indexKey];
366
+ if (!index) {
367
+ return {
368
+ depId: getDepId(dep),
369
+ depKind: 'process-success',
370
+ status: 'unsupported-kind',
371
+ unresolvedReason: `process-success index for '${dep.process}' (window=${(_b = dep.withinWindow) !== null && _b !== void 0 ? _b : '<no-window>'}) was not pre-fetched for this run; engine cannot evaluate.`,
372
+ };
373
+ }
374
+ const recordId = ctx.record.id;
375
+ if (!recordId) {
376
+ return {
377
+ depId: getDepId(dep),
378
+ depKind: 'process-success',
379
+ status: 'unsupported-kind',
380
+ unresolvedReason: 'record.id missing; cannot look up process-success record.',
381
+ };
382
+ }
383
+ const hit = index.get(recordId);
384
+ if (hit) {
385
+ return { depId: getDepId(dep), depKind: 'process-success', status: 'correct' };
386
+ }
387
+ const matchedClause = findProcessSuccessVarianceMatch(dep, 'no-process-record-found');
388
+ if (!matchedClause) {
389
+ return {
390
+ depId: getDepId(dep),
391
+ depKind: 'process-success',
392
+ status: 'unmatched-variance',
393
+ varianceActualSummary: `no SUCCESS '${dep.process}' record found within ${(_c = dep.withinWindow) !== null && _c !== void 0 ? _c : '<no-window>'} for record.id=${recordId}`,
394
+ unresolvedReason: `No SUCCESS '${dep.process}' record found and no onVariance clause matched 'no-process-record-found'.`,
395
+ };
396
+ }
397
+ return {
398
+ depId: getDepId(dep),
399
+ depKind: 'process-success',
400
+ status: 'variance',
401
+ varianceShape: 'no-process-record-found',
402
+ varianceActualSummary: `no SUCCESS '${dep.process}' record found within ${(_d = dep.withinWindow) !== null && _d !== void 0 ? _d : '<no-window>'}`,
403
+ prescribedAction: matchedClause,
404
+ };
405
+ }
406
+ function findProcessSuccessVarianceMatch(dep, shape) {
407
+ if (!dep.onVariance || dep.onVariance.length === 0)
408
+ return null;
409
+ for (const clause of dep.onVariance) {
410
+ if (clause.when === shape)
411
+ return clause.action;
412
+ }
413
+ for (const clause of dep.onVariance) {
414
+ if (clause.when === 'otherwise')
415
+ return clause.action;
416
+ }
417
+ return null;
418
+ }
419
+ // ──────────────────────────────────────────────────────────────────────
420
+ // Filter primitives
421
+ // ──────────────────────────────────────────────────────────────────────
422
+ function matchesAllFilters(record, filters) {
423
+ return filters.every((f) => matchesFilter(record, f));
424
+ }
425
+ function matchesFilter(record, filter) {
426
+ const value = readColumn(record, filter.column);
427
+ switch (filter.op) {
428
+ case 'eq':
429
+ return value === filter.value;
430
+ case 'notEq':
431
+ return value !== filter.value;
432
+ case 'in':
433
+ return Array.isArray(filter.value) && filter.value.includes(value);
434
+ case 'notIn':
435
+ return Array.isArray(filter.value) && !filter.value.includes(value);
436
+ case 'notNull':
437
+ return value !== null && value !== undefined;
438
+ case 'isNull':
439
+ return value === null || value === undefined;
440
+ case 'gte':
441
+ return (value !== null &&
442
+ value !== undefined &&
443
+ typeof value === typeof filter.value &&
444
+ value >= filter.value);
445
+ case 'lte':
446
+ return (value !== null &&
447
+ value !== undefined &&
448
+ typeof value === typeof filter.value &&
449
+ value <= filter.value);
450
+ }
451
+ }
452
+ function readColumn(record, column) {
453
+ var _a, _b, _c;
454
+ switch (column) {
455
+ case 'id':
456
+ return record.id;
457
+ case 'Type':
458
+ return record.type;
459
+ case 'StageKey':
460
+ return (_a = record.stage) === null || _a === void 0 ? void 0 : _a.key;
461
+ case 'StageName':
462
+ return (_b = record.stage) === null || _b === void 0 ? void 0 : _b.name;
463
+ case 'CreatedAt':
464
+ return record.createdAt;
465
+ case 'UpdatedAt':
466
+ return record.updatedAt;
467
+ case 'recordNumber':
468
+ return record.recordNumber;
469
+ default:
470
+ return (_c = record.properties) === null || _c === void 0 ? void 0 : _c[column];
471
+ }
472
+ }
473
+ // ──────────────────────────────────────────────────────────────────────
474
+ // Helpers
475
+ // ──────────────────────────────────────────────────────────────────────
476
+ function getDepId(dep) {
477
+ if ('id' in dep && dep.id)
478
+ return dep.id;
479
+ return `<no-id:${dep.kind}>`;
480
+ }
481
+ function summariseRecords(records, entity) {
482
+ if (records.length === 0)
483
+ return `0 records of ${entity}`;
484
+ const byType = records.reduce((acc, r) => {
485
+ var _a, _b;
486
+ const t = (_a = r.type) !== null && _a !== void 0 ? _a : '<no-type>';
487
+ acc[t] = ((_b = acc[t]) !== null && _b !== void 0 ? _b : 0) + 1;
488
+ return acc;
489
+ }, {});
490
+ const parts = Object.entries(byType).map(([t, n]) => `${n} ${t}`);
491
+ return `${records.length} record(s) of ${entity}: ${parts.join(', ')}`;
492
+ }
493
+ function describeFilters(filters) {
494
+ return filters
495
+ .map((f) => `${f.column} ${f.op}${f.value === undefined ? '' : ` ${JSON.stringify(f.value)}`}`)
496
+ .join(' AND ');
497
+ }
498
+ function computeOverall(verdicts) {
499
+ if (verdicts.length === 0)
500
+ return 'correct';
501
+ // Priority: variance > unmatched-variance > correct > unsupported-kind.
502
+ if (verdicts.some((v) => v.status === 'variance'))
503
+ return 'variance';
504
+ if (verdicts.some((v) => v.status === 'unmatched-variance'))
505
+ return 'unmatched-variance';
506
+ if (verdicts.some((v) => v.status === 'correct'))
507
+ return 'correct';
508
+ return 'unsupported-kind';
509
+ }
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Process-model evaluation types — the deployable subset of the agent-os
3
+ * authoring model `agent-os/knowledge/business-processes/types.ts`.
4
+ *
5
+ * data + process template + evaluation mechanism → correct / variance
6
+ *
7
+ * The authoring model carries rendering / BPMN / documentation fields
8
+ * (entryActions, handoffs, stageJobs, rag, owners, notifications). The
9
+ * EVALUATION engine reads only `stages[].{key, name, terminal, dependencies,
10
+ * warnAfter, alertAfter}` plus the dependency + onVariance shapes — so this
11
+ * lifted copy is deliberately slim: only what `validateAgainstProcess` reads
12
+ * and what a frozen `ProcessDef` projection needs to carry.
13
+ *
14
+ * Source of truth remains the agent-os authoring files. The `*-process.
15
+ * projection.ts` siblings are frozen mechanical extractions, each with a
16
+ * "verified against live BP on <date>" header (same pattern as
17
+ * `wholesale-provider/install-stage-kci-spec.ts`).
18
+ */
19
+ export interface PropertyFilter {
20
+ column: string;
21
+ op: 'eq' | 'in' | 'notIn' | 'notEq' | 'notNull' | 'isNull' | 'gte' | 'lte';
22
+ value?: string | number | string[];
23
+ }
24
+ export declare type Severity = 'INFO' | 'WARN' | 'BLOCKER';
25
+ export interface CrossSystemMapping {
26
+ /** Where the counterpart record lives, e.g. 'NET'. */
27
+ system: string;
28
+ /** The remote entity key, e.g. 'OrderModule:Order' on NET. */
29
+ remoteEntity: string;
30
+ /** Which YF-side property's value matches which remote-side property's value. */
31
+ mapping: {
32
+ fromProperty: string;
33
+ toProperty: string;
34
+ };
35
+ }
36
+ export declare type ActionDecl = {
37
+ kind: 'revert-parent-to';
38
+ stageKey: string;
39
+ properties?: Record<string, string | number | boolean | null>;
40
+ notes?: string;
41
+ } | {
42
+ kind: 'cancel-parent';
43
+ stageKey: string;
44
+ properties?: Record<string, string | number | boolean | null>;
45
+ notes?: string;
46
+ } | {
47
+ kind: 'advance-parent-to';
48
+ stageKey: string;
49
+ properties?: Record<string, string | number | boolean | null>;
50
+ notes?: string;
51
+ } | {
52
+ kind: 'manual-triage';
53
+ reason: string;
54
+ } | {
55
+ kind: 'invoke-platform-api';
56
+ target?: 'yf' | 'net';
57
+ endpoint: string;
58
+ method?: 'get' | 'post' | 'patch' | 'put' | 'delete';
59
+ payload?: Record<string, unknown>;
60
+ targetSelector?: PropertyFilter[];
61
+ notes?: string;
62
+ };
63
+ export declare type RelatedRecordVarianceClause = {
64
+ when: 'no-related-records';
65
+ action: ActionDecl;
66
+ } | {
67
+ when: 'no-records-matching';
68
+ match: PropertyFilter[];
69
+ action: ActionDecl;
70
+ } | {
71
+ when: 'all-records-matching-also-match';
72
+ selector: PropertyFilter[];
73
+ additional: PropertyFilter[];
74
+ action: ActionDecl;
75
+ } | {
76
+ when: 'otherwise';
77
+ action: ActionDecl;
78
+ };
79
+ export declare type ProcessSuccessVarianceClause = {
80
+ when: 'no-process-record-found';
81
+ action: ActionDecl;
82
+ } | {
83
+ when: 'process-record-failed';
84
+ action: ActionDecl;
85
+ } | {
86
+ when: 'process-record-stale';
87
+ action: ActionDecl;
88
+ } | {
89
+ when: 'otherwise';
90
+ action: ActionDecl;
91
+ };
92
+ export declare type CrossSystemRecordExistsVarianceClause = {
93
+ when: 'no-cross-system-record';
94
+ action: ActionDecl;
95
+ } | {
96
+ when: 'cross-system-record-mismatched';
97
+ action: ActionDecl;
98
+ } | {
99
+ when: 'otherwise';
100
+ action: ActionDecl;
101
+ };
102
+ export declare type InvariantVarianceClause = {
103
+ when: 'predicate-failed';
104
+ action: ActionDecl;
105
+ } | {
106
+ when: 'predicate-not-registered';
107
+ action: ActionDecl;
108
+ } | {
109
+ when: 'otherwise';
110
+ action: ActionDecl;
111
+ };
112
+ export interface ProcessSuccessDependency {
113
+ kind: 'process-success';
114
+ id?: string;
115
+ /** Process-execution `name` (action_key for actions, custom name for workflows). */
116
+ process: string;
117
+ matchOn: 'journeyId' | 'input.id';
118
+ withinWindow?: string;
119
+ severity: Severity;
120
+ notes?: string;
121
+ onVariance?: ProcessSuccessVarianceClause[];
122
+ }
123
+ export interface CrossSystemRecordExistsDependency {
124
+ kind: 'cross-system-record-exists';
125
+ id?: string;
126
+ parentFilter?: PropertyFilter[];
127
+ mapping: CrossSystemMapping;
128
+ remoteFilter?: PropertyFilter[];
129
+ severity: Severity;
130
+ notes?: string;
131
+ onVariance?: CrossSystemRecordExistsVarianceClause[];
132
+ }
133
+ export interface RelatedRecordDependency {
134
+ kind: 'related-record';
135
+ id?: string;
136
+ parentFilter?: PropertyFilter[];
137
+ relation: {
138
+ entity: string;
139
+ filter: PropertyFilter[];
140
+ minCount?: number;
141
+ maxCount?: number;
142
+ };
143
+ severity: Severity;
144
+ notes?: string;
145
+ onVariance?: RelatedRecordVarianceClause[];
146
+ }
147
+ export interface InvariantDependency {
148
+ kind: 'invariant';
149
+ id: string;
150
+ description: string;
151
+ severity: Severity;
152
+ predicateRef?: string;
153
+ onVariance?: InvariantVarianceClause[];
154
+ }
155
+ export declare type Dependency = ProcessSuccessDependency | CrossSystemRecordExistsDependency | RelatedRecordDependency | InvariantDependency;
156
+ export interface StageDef {
157
+ key: string;
158
+ name: string;
159
+ terminal?: boolean;
160
+ dependencies?: Dependency[];
161
+ /** Time-in-stage thresholds, e.g. '7d'. Drive the AgeInStage signal. */
162
+ warnAfter?: string;
163
+ alertAfter?: string;
164
+ }
165
+ export interface ProcessDef {
166
+ /** Process id (upper-snake), e.g. ORDER, INSTALL. */
167
+ id: string;
168
+ title: string;
169
+ /** Canonical entity key, e.g. 'OrderModule:Order'. */
170
+ entity: string;
171
+ /** Constraint scoping this process to a subset of the entity. */
172
+ scopeFilter?: PropertyFilter[];
173
+ stages: StageDef[];
174
+ }
175
+ export interface ProcessSuccessRecord {
176
+ id?: string;
177
+ name?: string;
178
+ status?: string;
179
+ startedAt?: string;
180
+ completedAt?: string;
181
+ journeyId?: string;
182
+ input?: {
183
+ id?: string;
184
+ } & Record<string, unknown>;
185
+ }