@higher.archi/boe 1.0.15 → 1.0.17
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/compare/compare.d.ts +37 -0
- package/dist/compare/compare.d.ts.map +1 -0
- package/dist/compare/compare.js +97 -0
- package/dist/compare/compare.js.map +1 -0
- package/dist/compare/index.d.ts +3 -0
- package/dist/compare/index.d.ts.map +1 -0
- package/dist/compare/index.js +7 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/compare/types.d.ts +38 -0
- package/dist/compare/types.d.ts.map +1 -0
- package/dist/compare/types.js +9 -0
- package/dist/compare/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/promotion/index.d.ts +3 -0
- package/dist/promotion/index.d.ts.map +1 -0
- package/dist/promotion/index.js +7 -0
- package/dist/promotion/index.js.map +1 -0
- package/dist/promotion/promotion.d.ts +37 -0
- package/dist/promotion/promotion.d.ts.map +1 -0
- package/dist/promotion/promotion.js +129 -0
- package/dist/promotion/promotion.js.map +1 -0
- package/dist/promotion/types.d.ts +38 -0
- package/dist/promotion/types.d.ts.map +1 -0
- package/dist/promotion/types.js +9 -0
- package/dist/promotion/types.js.map +1 -0
- package/package.json +1 -1
- package/src/compare/compare.ts +106 -0
- package/src/compare/index.ts +5 -0
- package/src/compare/types.ts +41 -0
- package/src/index.ts +12 -0
- package/src/promotion/index.ts +5 -0
- package/src/promotion/promotion.ts +147 -0
- package/src/promotion/types.ts +37 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruleset Comparison Utility
|
|
3
|
+
*
|
|
4
|
+
* Scores a set of entities against two rulesets and computes per-entity
|
|
5
|
+
* deltas and aggregate tier migration statistics. Designed for preview/dry-run
|
|
6
|
+
* workflows where a draft config is compared against the active config
|
|
7
|
+
* before activation.
|
|
8
|
+
*/
|
|
9
|
+
import type { CompiledScoringRuleSet, ScoringOptions, TierDefinition } from '../engines/scoring/types';
|
|
10
|
+
import type { CompareInput, ComparisonResult } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Compare two scoring rulesets by executing both against the same inputs.
|
|
13
|
+
*
|
|
14
|
+
* For each input, creates a fresh ScoringEngine, adds the facts, and executes
|
|
15
|
+
* against both rulesets. Returns per-entity deltas and aggregate statistics
|
|
16
|
+
* including tier migration counts.
|
|
17
|
+
*
|
|
18
|
+
* @param rulesetA - The baseline ruleset (e.g. active/current config)
|
|
19
|
+
* @param rulesetB - The candidate ruleset (e.g. draft/proposed config)
|
|
20
|
+
* @param inputs - Entity fact sets to score with both rulesets
|
|
21
|
+
* @param options - Optional scoring options passed to both executions
|
|
22
|
+
* @returns Comparison result with per-entity deltas and summary statistics
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const result = compareRulesets(activeRuleset, draftRuleset, [
|
|
27
|
+
* { entityId: 'user-1', facts: [{ type: 'User', data: { score: 750 } }] },
|
|
28
|
+
* { entityId: 'user-2', facts: [{ type: 'User', data: { score: 620 } }] },
|
|
29
|
+
* ]);
|
|
30
|
+
*
|
|
31
|
+
* console.log(`Avg score delta: ${result.summary.avgScoreDelta}`);
|
|
32
|
+
* console.log(`Tier changes: ${result.summary.tierChanges}`);
|
|
33
|
+
* console.log(`Migrations:`, result.summary.tierMigrations);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function compareRulesets<T extends TierDefinition = TierDefinition>(rulesetA: CompiledScoringRuleSet<T>, rulesetB: CompiledScoringRuleSet<T>, inputs: CompareInput[], options?: ScoringOptions): ComparisonResult<T>;
|
|
37
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/compare/compare.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACvG,OAAO,KAAK,EAAE,YAAY,EAAe,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EACvE,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,EACnC,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,EACnC,MAAM,EAAE,YAAY,EAAE,EACtB,OAAO,GAAE,cAAmB,GAC3B,gBAAgB,CAAC,CAAC,CAAC,CA8DrB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Ruleset Comparison Utility
|
|
4
|
+
*
|
|
5
|
+
* Scores a set of entities against two rulesets and computes per-entity
|
|
6
|
+
* deltas and aggregate tier migration statistics. Designed for preview/dry-run
|
|
7
|
+
* workflows where a draft config is compared against the active config
|
|
8
|
+
* before activation.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.compareRulesets = compareRulesets;
|
|
12
|
+
const engine_1 = require("../engines/scoring/engine");
|
|
13
|
+
/**
|
|
14
|
+
* Compare two scoring rulesets by executing both against the same inputs.
|
|
15
|
+
*
|
|
16
|
+
* For each input, creates a fresh ScoringEngine, adds the facts, and executes
|
|
17
|
+
* against both rulesets. Returns per-entity deltas and aggregate statistics
|
|
18
|
+
* including tier migration counts.
|
|
19
|
+
*
|
|
20
|
+
* @param rulesetA - The baseline ruleset (e.g. active/current config)
|
|
21
|
+
* @param rulesetB - The candidate ruleset (e.g. draft/proposed config)
|
|
22
|
+
* @param inputs - Entity fact sets to score with both rulesets
|
|
23
|
+
* @param options - Optional scoring options passed to both executions
|
|
24
|
+
* @returns Comparison result with per-entity deltas and summary statistics
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const result = compareRulesets(activeRuleset, draftRuleset, [
|
|
29
|
+
* { entityId: 'user-1', facts: [{ type: 'User', data: { score: 750 } }] },
|
|
30
|
+
* { entityId: 'user-2', facts: [{ type: 'User', data: { score: 620 } }] },
|
|
31
|
+
* ]);
|
|
32
|
+
*
|
|
33
|
+
* console.log(`Avg score delta: ${result.summary.avgScoreDelta}`);
|
|
34
|
+
* console.log(`Tier changes: ${result.summary.tierChanges}`);
|
|
35
|
+
* console.log(`Migrations:`, result.summary.tierMigrations);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function compareRulesets(rulesetA, rulesetB, inputs, options = {}) {
|
|
39
|
+
const deltas = [];
|
|
40
|
+
for (const input of inputs) {
|
|
41
|
+
const engineA = new engine_1.ScoringEngine();
|
|
42
|
+
for (const fact of input.facts) {
|
|
43
|
+
engineA.add(fact);
|
|
44
|
+
}
|
|
45
|
+
const resultA = engineA.execute(rulesetA, options);
|
|
46
|
+
const engineB = new engine_1.ScoringEngine();
|
|
47
|
+
for (const fact of input.facts) {
|
|
48
|
+
engineB.add(fact);
|
|
49
|
+
}
|
|
50
|
+
const resultB = engineB.execute(rulesetB, options);
|
|
51
|
+
deltas.push({
|
|
52
|
+
entityId: input.entityId,
|
|
53
|
+
scoreA: resultA.totalScore,
|
|
54
|
+
scoreB: resultB.totalScore,
|
|
55
|
+
scoreDelta: resultB.totalScore - resultA.totalScore,
|
|
56
|
+
tierA: resultA.tier?.id,
|
|
57
|
+
tierB: resultB.tier?.id,
|
|
58
|
+
tierChanged: resultA.tier?.id !== resultB.tier?.id,
|
|
59
|
+
resultA,
|
|
60
|
+
resultB,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// Compute summary
|
|
64
|
+
const entityCount = deltas.length;
|
|
65
|
+
let sumDelta = 0;
|
|
66
|
+
let minDelta = Infinity;
|
|
67
|
+
let maxDelta = -Infinity;
|
|
68
|
+
let tierChanges = 0;
|
|
69
|
+
const tierMigrations = {};
|
|
70
|
+
for (const d of deltas) {
|
|
71
|
+
sumDelta += d.scoreDelta;
|
|
72
|
+
if (d.scoreDelta < minDelta)
|
|
73
|
+
minDelta = d.scoreDelta;
|
|
74
|
+
if (d.scoreDelta > maxDelta)
|
|
75
|
+
maxDelta = d.scoreDelta;
|
|
76
|
+
if (d.tierChanged) {
|
|
77
|
+
tierChanges++;
|
|
78
|
+
const from = d.tierA ?? '(none)';
|
|
79
|
+
const to = d.tierB ?? '(none)';
|
|
80
|
+
if (!tierMigrations[from])
|
|
81
|
+
tierMigrations[from] = {};
|
|
82
|
+
tierMigrations[from][to] = (tierMigrations[from][to] ?? 0) + 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
deltas,
|
|
87
|
+
summary: {
|
|
88
|
+
entityCount,
|
|
89
|
+
avgScoreDelta: entityCount > 0 ? sumDelta / entityCount : 0,
|
|
90
|
+
minScoreDelta: entityCount > 0 ? minDelta : 0,
|
|
91
|
+
maxScoreDelta: entityCount > 0 ? maxDelta : 0,
|
|
92
|
+
tierChanges,
|
|
93
|
+
tierMigrations,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/compare/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compare/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG3E,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compareRulesets = void 0;
|
|
4
|
+
// Core comparison
|
|
5
|
+
var compare_1 = require("./compare");
|
|
6
|
+
Object.defineProperty(exports, "compareRulesets", { enumerable: true, get: function () { return compare_1.compareRulesets; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compare/index.ts"],"names":[],"mappings":";;;AAGA,kBAAkB;AAClB,qCAA4C;AAAnC,0GAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare Module Types
|
|
3
|
+
*
|
|
4
|
+
* Types for scoring ruleset comparison — computing per-entity score deltas
|
|
5
|
+
* and tier migration statistics between two rulesets.
|
|
6
|
+
*/
|
|
7
|
+
import type { ScoringResult, TierDefinition } from '../engines/scoring/types';
|
|
8
|
+
import type { FactInput } from '../core';
|
|
9
|
+
/** A single test case: a set of facts to score with both rulesets */
|
|
10
|
+
export type CompareInput = {
|
|
11
|
+
entityId: string;
|
|
12
|
+
facts: FactInput[];
|
|
13
|
+
};
|
|
14
|
+
/** Per-entity diff between ruleset A and B results */
|
|
15
|
+
export type EntityDelta<T extends TierDefinition = TierDefinition> = {
|
|
16
|
+
entityId: string;
|
|
17
|
+
scoreA: number;
|
|
18
|
+
scoreB: number;
|
|
19
|
+
scoreDelta: number;
|
|
20
|
+
tierA?: string;
|
|
21
|
+
tierB?: string;
|
|
22
|
+
tierChanged: boolean;
|
|
23
|
+
resultA: ScoringResult<T>;
|
|
24
|
+
resultB: ScoringResult<T>;
|
|
25
|
+
};
|
|
26
|
+
/** Aggregate comparison result */
|
|
27
|
+
export type ComparisonResult<T extends TierDefinition = TierDefinition> = {
|
|
28
|
+
deltas: EntityDelta<T>[];
|
|
29
|
+
summary: {
|
|
30
|
+
entityCount: number;
|
|
31
|
+
avgScoreDelta: number;
|
|
32
|
+
minScoreDelta: number;
|
|
33
|
+
maxScoreDelta: number;
|
|
34
|
+
tierChanges: number;
|
|
35
|
+
tierMigrations: Record<string, Record<string, number>>;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/compare/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF,sDAAsD;AACtD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CAC3B,CAAC;AAEF,kCAAkC;AAClC,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACxE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACzB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KACxD,CAAC;CACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Compare Module Types
|
|
4
|
+
*
|
|
5
|
+
* Types for scoring ruleset comparison — computing per-entity score deltas
|
|
6
|
+
* and tier migration statistics between two rulesets.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/compare/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
package/dist/index.d.ts
CHANGED
|
@@ -59,4 +59,6 @@ export { calculateDecayMultiplier, resolveDecayTimestamp } from './core/evaluati
|
|
|
59
59
|
export type { DecayCurve, DecayTimeUnit, DecayConfig, DecayInfo } from './core/evaluation/decay';
|
|
60
60
|
export * from './qfacts';
|
|
61
61
|
export * from './abtesting';
|
|
62
|
+
export * from './compare';
|
|
63
|
+
export * from './promotion';
|
|
62
64
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -187,4 +187,12 @@ __exportStar(require("./qfacts"), exports);
|
|
|
187
187
|
// A/B Testing - Deterministic Variant Resolution
|
|
188
188
|
// ========================================
|
|
189
189
|
__exportStar(require("./abtesting"), exports);
|
|
190
|
+
// ========================================
|
|
191
|
+
// Compare - Ruleset Comparison Utility
|
|
192
|
+
// ========================================
|
|
193
|
+
__exportStar(require("./compare"), exports);
|
|
194
|
+
// ========================================
|
|
195
|
+
// Promotion — Counterfactual Scoring Analysis
|
|
196
|
+
// ========================================
|
|
197
|
+
__exportStar(require("./promotion"), exports);
|
|
190
198
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGtF,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.promotionPath = void 0;
|
|
4
|
+
// Core promotion path
|
|
5
|
+
var promotion_1 = require("./promotion");
|
|
6
|
+
Object.defineProperty(exports, "promotionPath", { enumerable: true, get: function () { return promotion_1.promotionPath; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":";;;AAGA,sBAAsB;AACtB,yCAA4C;AAAnC,0GAAA,aAAa,OAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion Path — Counterfactual Scoring Analysis
|
|
3
|
+
*
|
|
4
|
+
* Analyzes unfired rules in a scoring result to generate ranked,
|
|
5
|
+
* actionable improvement roadmaps. Given a ruleset, facts, and a
|
|
6
|
+
* target (tier or score), identifies what scoring opportunities
|
|
7
|
+
* remain and how much each could contribute.
|
|
8
|
+
*/
|
|
9
|
+
import type { CompiledScoringRuleSet, ScoringOptions, TierDefinition } from '../engines/scoring/types';
|
|
10
|
+
import type { FactInput } from '../core';
|
|
11
|
+
import type { PromotionTarget, PromotionResult } from './types';
|
|
12
|
+
/**
|
|
13
|
+
* Compute a promotion path — the ranked set of unfired rules that could
|
|
14
|
+
* close the gap between a current score and a target score or tier.
|
|
15
|
+
*
|
|
16
|
+
* @param ruleset - Compiled scoring ruleset to analyze
|
|
17
|
+
* @param facts - Facts to score against the ruleset
|
|
18
|
+
* @param target - Target tier or score to reach
|
|
19
|
+
* @param options - Optional scoring options passed to engine execution
|
|
20
|
+
* @returns Promotion result with gap analysis and ranked opportunities
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const path = promotionPath(athleteRuleset, athleteFacts, { tier: 'premium' });
|
|
25
|
+
*
|
|
26
|
+
* console.log(`Current: ${path.currentScore} (${path.currentTier})`);
|
|
27
|
+
* console.log(`Target: ${path.targetScore} (${path.targetTier})`);
|
|
28
|
+
* console.log(`Gap: ${path.gap} points`);
|
|
29
|
+
*
|
|
30
|
+
* for (const opp of path.opportunities) {
|
|
31
|
+
* const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
32
|
+
* console.log(` ${opp.ruleId}: ${impact} — ${opp.reason ?? 'no reason'}`);
|
|
33
|
+
* }
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function promotionPath<T extends TierDefinition = TierDefinition>(ruleset: CompiledScoringRuleSet<T>, facts: FactInput[], target: PromotionTarget, options?: ScoringOptions): PromotionResult<T>;
|
|
37
|
+
//# sourceMappingURL=promotion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion.d.ts","sourceRoot":"","sources":["../../src/promotion/promotion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,sBAAsB,EACtB,cAAc,EACd,cAAc,EACf,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,eAAe,EAAwB,MAAM,SAAS,CAAC;AAEtF;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EACrE,OAAO,EAAE,sBAAsB,CAAC,CAAC,CAAC,EAClC,KAAK,EAAE,SAAS,EAAE,EAClB,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,cAAmB,GAC3B,eAAe,CAAC,CAAC,CAAC,CAmGpB"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Promotion Path — Counterfactual Scoring Analysis
|
|
4
|
+
*
|
|
5
|
+
* Analyzes unfired rules in a scoring result to generate ranked,
|
|
6
|
+
* actionable improvement roadmaps. Given a ruleset, facts, and a
|
|
7
|
+
* target (tier or score), identifies what scoring opportunities
|
|
8
|
+
* remain and how much each could contribute.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.promotionPath = promotionPath;
|
|
12
|
+
const engine_1 = require("../engines/scoring/engine");
|
|
13
|
+
/**
|
|
14
|
+
* Compute a promotion path — the ranked set of unfired rules that could
|
|
15
|
+
* close the gap between a current score and a target score or tier.
|
|
16
|
+
*
|
|
17
|
+
* @param ruleset - Compiled scoring ruleset to analyze
|
|
18
|
+
* @param facts - Facts to score against the ruleset
|
|
19
|
+
* @param target - Target tier or score to reach
|
|
20
|
+
* @param options - Optional scoring options passed to engine execution
|
|
21
|
+
* @returns Promotion result with gap analysis and ranked opportunities
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const path = promotionPath(athleteRuleset, athleteFacts, { tier: 'premium' });
|
|
26
|
+
*
|
|
27
|
+
* console.log(`Current: ${path.currentScore} (${path.currentTier})`);
|
|
28
|
+
* console.log(`Target: ${path.targetScore} (${path.targetTier})`);
|
|
29
|
+
* console.log(`Gap: ${path.gap} points`);
|
|
30
|
+
*
|
|
31
|
+
* for (const opp of path.opportunities) {
|
|
32
|
+
* const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
33
|
+
* console.log(` ${opp.ruleId}: ${impact} — ${opp.reason ?? 'no reason'}`);
|
|
34
|
+
* }
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
function promotionPath(ruleset, facts, target, options = {}) {
|
|
38
|
+
const start = performance.now();
|
|
39
|
+
// 1. Execute scoring engine
|
|
40
|
+
const engine = new engine_1.ScoringEngine();
|
|
41
|
+
for (const fact of facts) {
|
|
42
|
+
engine.add(fact);
|
|
43
|
+
}
|
|
44
|
+
const result = engine.execute(ruleset, options);
|
|
45
|
+
// 2. Resolve target score
|
|
46
|
+
let targetScore;
|
|
47
|
+
let targetTier;
|
|
48
|
+
if ('tier' in target && target.tier !== undefined) {
|
|
49
|
+
targetTier = target.tier;
|
|
50
|
+
const tiers = ruleset.config.tiers;
|
|
51
|
+
if (!tiers || tiers.length === 0) {
|
|
52
|
+
throw new Error(`Cannot target tier "${target.tier}": no tiers configured in ruleset`);
|
|
53
|
+
}
|
|
54
|
+
const tierDef = tiers.find(t => t.id === target.tier);
|
|
55
|
+
if (!tierDef) {
|
|
56
|
+
const available = tiers.map(t => t.id).join(', ');
|
|
57
|
+
throw new Error(`Tier "${target.tier}" not found. Available: ${available}`);
|
|
58
|
+
}
|
|
59
|
+
const threshold = tierDef.threshold;
|
|
60
|
+
targetScore = threshold === '-Infinity' ? 0 : threshold;
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
targetScore = target.score;
|
|
64
|
+
}
|
|
65
|
+
// 3. Compute gap
|
|
66
|
+
const currentScore = result.totalScore;
|
|
67
|
+
const gap = Math.max(0, targetScore - currentScore);
|
|
68
|
+
const alreadyMet = currentScore >= targetScore;
|
|
69
|
+
// 4. Identify unfired rules and extract opportunities
|
|
70
|
+
const firedSet = new Set(result.fired);
|
|
71
|
+
const opportunities = [];
|
|
72
|
+
for (const rule of ruleset.rules) {
|
|
73
|
+
if (firedSet.has(rule.id))
|
|
74
|
+
continue;
|
|
75
|
+
const score = rule.action.score;
|
|
76
|
+
const weight = rule.action.weight ?? 1;
|
|
77
|
+
let potentialScore;
|
|
78
|
+
let dynamic;
|
|
79
|
+
if (typeof score === 'number') {
|
|
80
|
+
potentialScore = score * weight;
|
|
81
|
+
dynamic = false;
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
// string (function name) or CompiledCondition (expression)
|
|
85
|
+
potentialScore = null;
|
|
86
|
+
dynamic = true;
|
|
87
|
+
}
|
|
88
|
+
opportunities.push({
|
|
89
|
+
ruleId: rule.id,
|
|
90
|
+
ruleName: rule.name,
|
|
91
|
+
category: rule.category,
|
|
92
|
+
potentialScore,
|
|
93
|
+
reason: rule.action.reason,
|
|
94
|
+
dynamic,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// 5. Sort: static scores descending, dynamic (nulls) at end
|
|
98
|
+
opportunities.sort((a, b) => {
|
|
99
|
+
if (a.potentialScore !== null && b.potentialScore !== null) {
|
|
100
|
+
return b.potentialScore - a.potentialScore;
|
|
101
|
+
}
|
|
102
|
+
if (a.potentialScore !== null)
|
|
103
|
+
return -1;
|
|
104
|
+
if (b.potentialScore !== null)
|
|
105
|
+
return 1;
|
|
106
|
+
return 0;
|
|
107
|
+
});
|
|
108
|
+
// 6. Sum static recoverable scores
|
|
109
|
+
let totalRecoverable = 0;
|
|
110
|
+
for (const opp of opportunities) {
|
|
111
|
+
if (opp.potentialScore !== null) {
|
|
112
|
+
totalRecoverable += opp.potentialScore;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const executionTimeMs = performance.now() - start;
|
|
116
|
+
return {
|
|
117
|
+
currentScore,
|
|
118
|
+
currentTier: result.tier?.id,
|
|
119
|
+
targetScore,
|
|
120
|
+
targetTier,
|
|
121
|
+
gap,
|
|
122
|
+
alreadyMet,
|
|
123
|
+
opportunities,
|
|
124
|
+
totalRecoverable,
|
|
125
|
+
result,
|
|
126
|
+
executionTimeMs,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=promotion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"promotion.js","sourceRoot":"","sources":["../../src/promotion/promotion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion Path Types
|
|
3
|
+
*
|
|
4
|
+
* Types for counterfactual scoring analysis — identifying unfired rules
|
|
5
|
+
* and ranking them by potential impact to generate promotion roadmaps.
|
|
6
|
+
*/
|
|
7
|
+
import type { ScoringResult, TierDefinition } from '../engines/scoring/types';
|
|
8
|
+
/** A single unfired rule representing a scoring opportunity */
|
|
9
|
+
export type PromotionOpportunity = {
|
|
10
|
+
ruleId: string;
|
|
11
|
+
ruleName?: string;
|
|
12
|
+
category?: string;
|
|
13
|
+
potentialScore: number | null;
|
|
14
|
+
reason?: string;
|
|
15
|
+
dynamic: boolean;
|
|
16
|
+
};
|
|
17
|
+
/** Target specification — reach a tier or a raw score threshold */
|
|
18
|
+
export type PromotionTarget = {
|
|
19
|
+
tier: string;
|
|
20
|
+
score?: never;
|
|
21
|
+
} | {
|
|
22
|
+
score: number;
|
|
23
|
+
tier?: never;
|
|
24
|
+
};
|
|
25
|
+
/** Promotion path result */
|
|
26
|
+
export type PromotionResult<T extends TierDefinition = TierDefinition> = {
|
|
27
|
+
currentScore: number;
|
|
28
|
+
currentTier?: string;
|
|
29
|
+
targetScore: number;
|
|
30
|
+
targetTier?: string;
|
|
31
|
+
gap: number;
|
|
32
|
+
alreadyMet: boolean;
|
|
33
|
+
opportunities: PromotionOpportunity[];
|
|
34
|
+
totalRecoverable: number;
|
|
35
|
+
result: ScoringResult<T>;
|
|
36
|
+
executionTimeMs: number;
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/promotion/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE9E,+DAA+D;AAC/D,MAAM,MAAM,oBAAoB,GAAG;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,mEAAmE;AACnE,MAAM,MAAM,eAAe,GACvB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,GAC/B;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpC,4BAA4B;AAC5B,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACvE,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,oBAAoB,EAAE,CAAC;IACtC,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;CACzB,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Promotion Path Types
|
|
4
|
+
*
|
|
5
|
+
* Types for counterfactual scoring analysis — identifying unfired rules
|
|
6
|
+
* and ranking them by potential impact to generate promotion roadmaps.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/promotion/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@higher.archi/boe",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.17",
|
|
4
4
|
"description": "A multi-strategy rule engine supporting forward chaining, backward chaining, scoring, sequential, fuzzy logic, and Bayesian inference",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruleset Comparison Utility
|
|
3
|
+
*
|
|
4
|
+
* Scores a set of entities against two rulesets and computes per-entity
|
|
5
|
+
* deltas and aggregate tier migration statistics. Designed for preview/dry-run
|
|
6
|
+
* workflows where a draft config is compared against the active config
|
|
7
|
+
* before activation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ScoringEngine } from '../engines/scoring/engine';
|
|
11
|
+
import type { CompiledScoringRuleSet, ScoringOptions, TierDefinition } from '../engines/scoring/types';
|
|
12
|
+
import type { CompareInput, EntityDelta, ComparisonResult } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Compare two scoring rulesets by executing both against the same inputs.
|
|
16
|
+
*
|
|
17
|
+
* For each input, creates a fresh ScoringEngine, adds the facts, and executes
|
|
18
|
+
* against both rulesets. Returns per-entity deltas and aggregate statistics
|
|
19
|
+
* including tier migration counts.
|
|
20
|
+
*
|
|
21
|
+
* @param rulesetA - The baseline ruleset (e.g. active/current config)
|
|
22
|
+
* @param rulesetB - The candidate ruleset (e.g. draft/proposed config)
|
|
23
|
+
* @param inputs - Entity fact sets to score with both rulesets
|
|
24
|
+
* @param options - Optional scoring options passed to both executions
|
|
25
|
+
* @returns Comparison result with per-entity deltas and summary statistics
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const result = compareRulesets(activeRuleset, draftRuleset, [
|
|
30
|
+
* { entityId: 'user-1', facts: [{ type: 'User', data: { score: 750 } }] },
|
|
31
|
+
* { entityId: 'user-2', facts: [{ type: 'User', data: { score: 620 } }] },
|
|
32
|
+
* ]);
|
|
33
|
+
*
|
|
34
|
+
* console.log(`Avg score delta: ${result.summary.avgScoreDelta}`);
|
|
35
|
+
* console.log(`Tier changes: ${result.summary.tierChanges}`);
|
|
36
|
+
* console.log(`Migrations:`, result.summary.tierMigrations);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function compareRulesets<T extends TierDefinition = TierDefinition>(
|
|
40
|
+
rulesetA: CompiledScoringRuleSet<T>,
|
|
41
|
+
rulesetB: CompiledScoringRuleSet<T>,
|
|
42
|
+
inputs: CompareInput[],
|
|
43
|
+
options: ScoringOptions = {}
|
|
44
|
+
): ComparisonResult<T> {
|
|
45
|
+
const deltas: EntityDelta<T>[] = [];
|
|
46
|
+
|
|
47
|
+
for (const input of inputs) {
|
|
48
|
+
const engineA = new ScoringEngine();
|
|
49
|
+
for (const fact of input.facts) {
|
|
50
|
+
engineA.add(fact);
|
|
51
|
+
}
|
|
52
|
+
const resultA = engineA.execute(rulesetA, options);
|
|
53
|
+
|
|
54
|
+
const engineB = new ScoringEngine();
|
|
55
|
+
for (const fact of input.facts) {
|
|
56
|
+
engineB.add(fact);
|
|
57
|
+
}
|
|
58
|
+
const resultB = engineB.execute(rulesetB, options);
|
|
59
|
+
|
|
60
|
+
deltas.push({
|
|
61
|
+
entityId: input.entityId,
|
|
62
|
+
scoreA: resultA.totalScore,
|
|
63
|
+
scoreB: resultB.totalScore,
|
|
64
|
+
scoreDelta: resultB.totalScore - resultA.totalScore,
|
|
65
|
+
tierA: resultA.tier?.id,
|
|
66
|
+
tierB: resultB.tier?.id,
|
|
67
|
+
tierChanged: resultA.tier?.id !== resultB.tier?.id,
|
|
68
|
+
resultA,
|
|
69
|
+
resultB,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Compute summary
|
|
74
|
+
const entityCount = deltas.length;
|
|
75
|
+
let sumDelta = 0;
|
|
76
|
+
let minDelta = Infinity;
|
|
77
|
+
let maxDelta = -Infinity;
|
|
78
|
+
let tierChanges = 0;
|
|
79
|
+
const tierMigrations: Record<string, Record<string, number>> = {};
|
|
80
|
+
|
|
81
|
+
for (const d of deltas) {
|
|
82
|
+
sumDelta += d.scoreDelta;
|
|
83
|
+
if (d.scoreDelta < minDelta) minDelta = d.scoreDelta;
|
|
84
|
+
if (d.scoreDelta > maxDelta) maxDelta = d.scoreDelta;
|
|
85
|
+
|
|
86
|
+
if (d.tierChanged) {
|
|
87
|
+
tierChanges++;
|
|
88
|
+
const from = d.tierA ?? '(none)';
|
|
89
|
+
const to = d.tierB ?? '(none)';
|
|
90
|
+
if (!tierMigrations[from]) tierMigrations[from] = {};
|
|
91
|
+
tierMigrations[from][to] = (tierMigrations[from][to] ?? 0) + 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
deltas,
|
|
97
|
+
summary: {
|
|
98
|
+
entityCount,
|
|
99
|
+
avgScoreDelta: entityCount > 0 ? sumDelta / entityCount : 0,
|
|
100
|
+
minScoreDelta: entityCount > 0 ? minDelta : 0,
|
|
101
|
+
maxScoreDelta: entityCount > 0 ? maxDelta : 0,
|
|
102
|
+
tierChanges,
|
|
103
|
+
tierMigrations,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare Module Types
|
|
3
|
+
*
|
|
4
|
+
* Types for scoring ruleset comparison — computing per-entity score deltas
|
|
5
|
+
* and tier migration statistics between two rulesets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ScoringResult, TierDefinition } from '../engines/scoring/types';
|
|
9
|
+
import type { FactInput } from '../core';
|
|
10
|
+
|
|
11
|
+
/** A single test case: a set of facts to score with both rulesets */
|
|
12
|
+
export type CompareInput = {
|
|
13
|
+
entityId: string;
|
|
14
|
+
facts: FactInput[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Per-entity diff between ruleset A and B results */
|
|
18
|
+
export type EntityDelta<T extends TierDefinition = TierDefinition> = {
|
|
19
|
+
entityId: string;
|
|
20
|
+
scoreA: number;
|
|
21
|
+
scoreB: number;
|
|
22
|
+
scoreDelta: number;
|
|
23
|
+
tierA?: string;
|
|
24
|
+
tierB?: string;
|
|
25
|
+
tierChanged: boolean;
|
|
26
|
+
resultA: ScoringResult<T>;
|
|
27
|
+
resultB: ScoringResult<T>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Aggregate comparison result */
|
|
31
|
+
export type ComparisonResult<T extends TierDefinition = TierDefinition> = {
|
|
32
|
+
deltas: EntityDelta<T>[];
|
|
33
|
+
summary: {
|
|
34
|
+
entityCount: number;
|
|
35
|
+
avgScoreDelta: number;
|
|
36
|
+
minScoreDelta: number;
|
|
37
|
+
maxScoreDelta: number;
|
|
38
|
+
tierChanges: number;
|
|
39
|
+
tierMigrations: Record<string, Record<string, number>>;
|
|
40
|
+
};
|
|
41
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -412,3 +412,15 @@ export * from './qfacts';
|
|
|
412
412
|
// ========================================
|
|
413
413
|
|
|
414
414
|
export * from './abtesting';
|
|
415
|
+
|
|
416
|
+
// ========================================
|
|
417
|
+
// Compare - Ruleset Comparison Utility
|
|
418
|
+
// ========================================
|
|
419
|
+
|
|
420
|
+
export * from './compare';
|
|
421
|
+
|
|
422
|
+
// ========================================
|
|
423
|
+
// Promotion — Counterfactual Scoring Analysis
|
|
424
|
+
// ========================================
|
|
425
|
+
|
|
426
|
+
export * from './promotion';
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion Path — Counterfactual Scoring Analysis
|
|
3
|
+
*
|
|
4
|
+
* Analyzes unfired rules in a scoring result to generate ranked,
|
|
5
|
+
* actionable improvement roadmaps. Given a ruleset, facts, and a
|
|
6
|
+
* target (tier or score), identifies what scoring opportunities
|
|
7
|
+
* remain and how much each could contribute.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ScoringEngine } from '../engines/scoring/engine';
|
|
11
|
+
import type {
|
|
12
|
+
CompiledScoringRuleSet,
|
|
13
|
+
ScoringOptions,
|
|
14
|
+
TierDefinition
|
|
15
|
+
} from '../engines/scoring/types';
|
|
16
|
+
import type { FactInput } from '../core';
|
|
17
|
+
import type { PromotionTarget, PromotionResult, PromotionOpportunity } from './types';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Compute a promotion path — the ranked set of unfired rules that could
|
|
21
|
+
* close the gap between a current score and a target score or tier.
|
|
22
|
+
*
|
|
23
|
+
* @param ruleset - Compiled scoring ruleset to analyze
|
|
24
|
+
* @param facts - Facts to score against the ruleset
|
|
25
|
+
* @param target - Target tier or score to reach
|
|
26
|
+
* @param options - Optional scoring options passed to engine execution
|
|
27
|
+
* @returns Promotion result with gap analysis and ranked opportunities
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const path = promotionPath(athleteRuleset, athleteFacts, { tier: 'premium' });
|
|
32
|
+
*
|
|
33
|
+
* console.log(`Current: ${path.currentScore} (${path.currentTier})`);
|
|
34
|
+
* console.log(`Target: ${path.targetScore} (${path.targetTier})`);
|
|
35
|
+
* console.log(`Gap: ${path.gap} points`);
|
|
36
|
+
*
|
|
37
|
+
* for (const opp of path.opportunities) {
|
|
38
|
+
* const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
|
|
39
|
+
* console.log(` ${opp.ruleId}: ${impact} — ${opp.reason ?? 'no reason'}`);
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export function promotionPath<T extends TierDefinition = TierDefinition>(
|
|
44
|
+
ruleset: CompiledScoringRuleSet<T>,
|
|
45
|
+
facts: FactInput[],
|
|
46
|
+
target: PromotionTarget,
|
|
47
|
+
options: ScoringOptions = {}
|
|
48
|
+
): PromotionResult<T> {
|
|
49
|
+
const start = performance.now();
|
|
50
|
+
|
|
51
|
+
// 1. Execute scoring engine
|
|
52
|
+
const engine = new ScoringEngine();
|
|
53
|
+
for (const fact of facts) {
|
|
54
|
+
engine.add(fact);
|
|
55
|
+
}
|
|
56
|
+
const result = engine.execute(ruleset, options);
|
|
57
|
+
|
|
58
|
+
// 2. Resolve target score
|
|
59
|
+
let targetScore: number;
|
|
60
|
+
let targetTier: string | undefined;
|
|
61
|
+
|
|
62
|
+
if ('tier' in target && target.tier !== undefined) {
|
|
63
|
+
targetTier = target.tier;
|
|
64
|
+
const tiers = ruleset.config.tiers;
|
|
65
|
+
if (!tiers || tiers.length === 0) {
|
|
66
|
+
throw new Error(`Cannot target tier "${target.tier}": no tiers configured in ruleset`);
|
|
67
|
+
}
|
|
68
|
+
const tierDef = tiers.find(t => t.id === target.tier);
|
|
69
|
+
if (!tierDef) {
|
|
70
|
+
const available = tiers.map(t => t.id).join(', ');
|
|
71
|
+
throw new Error(`Tier "${target.tier}" not found. Available: ${available}`);
|
|
72
|
+
}
|
|
73
|
+
const threshold = tierDef.threshold;
|
|
74
|
+
targetScore = threshold === '-Infinity' ? 0 : threshold;
|
|
75
|
+
} else {
|
|
76
|
+
targetScore = target.score;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// 3. Compute gap
|
|
80
|
+
const currentScore = result.totalScore;
|
|
81
|
+
const gap = Math.max(0, targetScore - currentScore);
|
|
82
|
+
const alreadyMet = currentScore >= targetScore;
|
|
83
|
+
|
|
84
|
+
// 4. Identify unfired rules and extract opportunities
|
|
85
|
+
const firedSet = new Set(result.fired);
|
|
86
|
+
const opportunities: PromotionOpportunity[] = [];
|
|
87
|
+
|
|
88
|
+
for (const rule of ruleset.rules) {
|
|
89
|
+
if (firedSet.has(rule.id)) continue;
|
|
90
|
+
|
|
91
|
+
const score = rule.action.score;
|
|
92
|
+
const weight = rule.action.weight ?? 1;
|
|
93
|
+
let potentialScore: number | null;
|
|
94
|
+
let dynamic: boolean;
|
|
95
|
+
|
|
96
|
+
if (typeof score === 'number') {
|
|
97
|
+
potentialScore = score * weight;
|
|
98
|
+
dynamic = false;
|
|
99
|
+
} else {
|
|
100
|
+
// string (function name) or CompiledCondition (expression)
|
|
101
|
+
potentialScore = null;
|
|
102
|
+
dynamic = true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
opportunities.push({
|
|
106
|
+
ruleId: rule.id,
|
|
107
|
+
ruleName: rule.name,
|
|
108
|
+
category: rule.category,
|
|
109
|
+
potentialScore,
|
|
110
|
+
reason: rule.action.reason,
|
|
111
|
+
dynamic,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// 5. Sort: static scores descending, dynamic (nulls) at end
|
|
116
|
+
opportunities.sort((a, b) => {
|
|
117
|
+
if (a.potentialScore !== null && b.potentialScore !== null) {
|
|
118
|
+
return b.potentialScore - a.potentialScore;
|
|
119
|
+
}
|
|
120
|
+
if (a.potentialScore !== null) return -1;
|
|
121
|
+
if (b.potentialScore !== null) return 1;
|
|
122
|
+
return 0;
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// 6. Sum static recoverable scores
|
|
126
|
+
let totalRecoverable = 0;
|
|
127
|
+
for (const opp of opportunities) {
|
|
128
|
+
if (opp.potentialScore !== null) {
|
|
129
|
+
totalRecoverable += opp.potentialScore;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const executionTimeMs = performance.now() - start;
|
|
134
|
+
|
|
135
|
+
return {
|
|
136
|
+
currentScore,
|
|
137
|
+
currentTier: result.tier?.id,
|
|
138
|
+
targetScore,
|
|
139
|
+
targetTier,
|
|
140
|
+
gap,
|
|
141
|
+
alreadyMet,
|
|
142
|
+
opportunities,
|
|
143
|
+
totalRecoverable,
|
|
144
|
+
result,
|
|
145
|
+
executionTimeMs,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promotion Path Types
|
|
3
|
+
*
|
|
4
|
+
* Types for counterfactual scoring analysis — identifying unfired rules
|
|
5
|
+
* and ranking them by potential impact to generate promotion roadmaps.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ScoringResult, TierDefinition } from '../engines/scoring/types';
|
|
9
|
+
|
|
10
|
+
/** A single unfired rule representing a scoring opportunity */
|
|
11
|
+
export type PromotionOpportunity = {
|
|
12
|
+
ruleId: string;
|
|
13
|
+
ruleName?: string;
|
|
14
|
+
category?: string;
|
|
15
|
+
potentialScore: number | null; // null = dynamic (expression/function)
|
|
16
|
+
reason?: string; // from ScoringAction.reason
|
|
17
|
+
dynamic: boolean; // true if score is expression/function
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/** Target specification — reach a tier or a raw score threshold */
|
|
21
|
+
export type PromotionTarget =
|
|
22
|
+
| { tier: string; score?: never }
|
|
23
|
+
| { score: number; tier?: never };
|
|
24
|
+
|
|
25
|
+
/** Promotion path result */
|
|
26
|
+
export type PromotionResult<T extends TierDefinition = TierDefinition> = {
|
|
27
|
+
currentScore: number;
|
|
28
|
+
currentTier?: string;
|
|
29
|
+
targetScore: number; // resolved threshold of target tier, or explicit score
|
|
30
|
+
targetTier?: string;
|
|
31
|
+
gap: number; // targetScore - currentScore (0 if already met)
|
|
32
|
+
alreadyMet: boolean; // true if current score/tier already meets target
|
|
33
|
+
opportunities: PromotionOpportunity[]; // ranked by potentialScore desc (nulls last)
|
|
34
|
+
totalRecoverable: number; // sum of all static potentialScores
|
|
35
|
+
result: ScoringResult<T>; // the full scoring result for reference
|
|
36
|
+
executionTimeMs: number;
|
|
37
|
+
};
|