@higher.archi/boe 1.0.16 → 1.0.18

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/index.d.ts CHANGED
@@ -60,4 +60,5 @@ export type { DecayCurve, DecayTimeUnit, DecayConfig, DecayInfo } from './core/e
60
60
  export * from './qfacts';
61
61
  export * from './abtesting';
62
62
  export * from './compare';
63
+ export * from './promotion';
63
64
  //# sourceMappingURL=index.d.ts.map
@@ -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
@@ -191,4 +191,8 @@ __exportStar(require("./abtesting"), exports);
191
191
  // Compare - Ruleset Comparison Utility
192
192
  // ========================================
193
193
  __exportStar(require("./compare"), exports);
194
+ // ========================================
195
+ // Promotion — Counterfactual Scoring Analysis
196
+ // ========================================
197
+ __exportStar(require("./promotion"), exports);
194
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,3 @@
1
+ export type { PromotionOpportunity, PromotionTarget, PromotionResult, PromotionSummaryOptions, CategorySummaryOptions } from './types';
2
+ export { promotionPath, summarizePromotionPath, summarizeCategoryBreakdown } from './promotion';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -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,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAGvI,OAAO,EAAE,aAAa,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.summarizeCategoryBreakdown = exports.summarizePromotionPath = 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
+ Object.defineProperty(exports, "summarizePromotionPath", { enumerable: true, get: function () { return promotion_1.summarizePromotionPath; } });
8
+ Object.defineProperty(exports, "summarizeCategoryBreakdown", { enumerable: true, get: function () { return promotion_1.summarizeCategoryBreakdown; } });
9
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/promotion/index.ts"],"names":[],"mappings":";;;AAGA,sBAAsB;AACtB,yCAAgG;AAAvF,0GAAA,aAAa,OAAA;AAAE,mHAAA,sBAAsB,OAAA;AAAE,uHAAA,0BAA0B,OAAA"}
@@ -0,0 +1,68 @@
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, PromotionSummaryOptions, CategorySummaryOptions } 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(vendorRuleset, vendorFacts, { tier: 'preferred' });
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
+ /**
38
+ * Generate a human-readable summary string from a PromotionResult.
39
+ *
40
+ * @param path - The promotion result to summarize
41
+ * @param options - Optional display settings
42
+ * @returns Formatted multi-line summary string
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const path = promotionPath(ruleset, facts, { tier: 'preferred' });
47
+ * console.log(summarizePromotionPath(path));
48
+ * ```
49
+ */
50
+ export declare function summarizePromotionPath(path: PromotionResult, options?: PromotionSummaryOptions): string;
51
+ /**
52
+ * Generate a human-readable category breakdown from a PromotionResult.
53
+ *
54
+ * Groups opportunities by category and shows per-category recoverable
55
+ * points with individual opportunity details.
56
+ *
57
+ * @param path - The promotion result to summarize
58
+ * @param options - Optional display settings
59
+ * @returns Formatted multi-line category breakdown string
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * const path = promotionPath(ruleset, facts, { tier: 'elite' });
64
+ * console.log(summarizeCategoryBreakdown(path));
65
+ * ```
66
+ */
67
+ export declare function summarizeCategoryBreakdown(path: PromotionResult, options?: CategorySummaryOptions): string;
68
+ //# 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,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAEvI;;;;;;;;;;;;;;;;;;;;;;;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;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE,uBAA4B,GACpC,MAAM,CAqCR;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,eAAe,EACrB,OAAO,GAAE,sBAA2B,GACnC,MAAM,CAqCR"}
@@ -0,0 +1,225 @@
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
+ exports.summarizePromotionPath = summarizePromotionPath;
13
+ exports.summarizeCategoryBreakdown = summarizeCategoryBreakdown;
14
+ const engine_1 = require("../engines/scoring/engine");
15
+ /**
16
+ * Compute a promotion path — the ranked set of unfired rules that could
17
+ * close the gap between a current score and a target score or tier.
18
+ *
19
+ * @param ruleset - Compiled scoring ruleset to analyze
20
+ * @param facts - Facts to score against the ruleset
21
+ * @param target - Target tier or score to reach
22
+ * @param options - Optional scoring options passed to engine execution
23
+ * @returns Promotion result with gap analysis and ranked opportunities
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * const path = promotionPath(vendorRuleset, vendorFacts, { tier: 'preferred' });
28
+ *
29
+ * console.log(`Current: ${path.currentScore} (${path.currentTier})`);
30
+ * console.log(`Target: ${path.targetScore} (${path.targetTier})`);
31
+ * console.log(`Gap: ${path.gap} points`);
32
+ *
33
+ * for (const opp of path.opportunities) {
34
+ * const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
35
+ * console.log(` ${opp.ruleId}: ${impact} — ${opp.reason ?? 'no reason'}`);
36
+ * }
37
+ * ```
38
+ */
39
+ function promotionPath(ruleset, facts, target, options = {}) {
40
+ const start = performance.now();
41
+ // 1. Execute scoring engine
42
+ const engine = new engine_1.ScoringEngine();
43
+ for (const fact of facts) {
44
+ engine.add(fact);
45
+ }
46
+ const result = engine.execute(ruleset, options);
47
+ // 2. Resolve target score
48
+ let targetScore;
49
+ let targetTier;
50
+ if ('tier' in target && target.tier !== undefined) {
51
+ targetTier = target.tier;
52
+ const tiers = ruleset.config.tiers;
53
+ if (!tiers || tiers.length === 0) {
54
+ throw new Error(`Cannot target tier "${target.tier}": no tiers configured in ruleset`);
55
+ }
56
+ const tierDef = tiers.find(t => t.id === target.tier);
57
+ if (!tierDef) {
58
+ const available = tiers.map(t => t.id).join(', ');
59
+ throw new Error(`Tier "${target.tier}" not found. Available: ${available}`);
60
+ }
61
+ const threshold = tierDef.threshold;
62
+ targetScore = threshold === '-Infinity' ? 0 : threshold;
63
+ }
64
+ else {
65
+ targetScore = target.score;
66
+ }
67
+ // 3. Compute gap
68
+ const currentScore = result.totalScore;
69
+ const gap = Math.max(0, targetScore - currentScore);
70
+ const alreadyMet = currentScore >= targetScore;
71
+ // 4. Identify unfired rules and extract opportunities
72
+ const firedSet = new Set(result.fired);
73
+ const opportunities = [];
74
+ for (const rule of ruleset.rules) {
75
+ if (firedSet.has(rule.id))
76
+ continue;
77
+ const score = rule.action.score;
78
+ const weight = rule.action.weight ?? 1;
79
+ let potentialScore;
80
+ let dynamic;
81
+ if (typeof score === 'number') {
82
+ potentialScore = score * weight;
83
+ dynamic = false;
84
+ }
85
+ else {
86
+ // string (function name) or CompiledCondition (expression)
87
+ potentialScore = null;
88
+ dynamic = true;
89
+ }
90
+ opportunities.push({
91
+ ruleId: rule.id,
92
+ ruleName: rule.name,
93
+ category: rule.category,
94
+ potentialScore,
95
+ reason: rule.action.reason,
96
+ dynamic,
97
+ });
98
+ }
99
+ // 5. Sort: static scores descending, dynamic (nulls) at end
100
+ opportunities.sort((a, b) => {
101
+ if (a.potentialScore !== null && b.potentialScore !== null) {
102
+ return b.potentialScore - a.potentialScore;
103
+ }
104
+ if (a.potentialScore !== null)
105
+ return -1;
106
+ if (b.potentialScore !== null)
107
+ return 1;
108
+ return 0;
109
+ });
110
+ // 6. Sum static recoverable scores
111
+ let totalRecoverable = 0;
112
+ for (const opp of opportunities) {
113
+ if (opp.potentialScore !== null) {
114
+ totalRecoverable += opp.potentialScore;
115
+ }
116
+ }
117
+ const executionTimeMs = performance.now() - start;
118
+ return {
119
+ currentScore,
120
+ currentTier: result.tier?.id,
121
+ targetScore,
122
+ targetTier,
123
+ gap,
124
+ alreadyMet,
125
+ opportunities,
126
+ totalRecoverable,
127
+ result,
128
+ executionTimeMs,
129
+ };
130
+ }
131
+ /**
132
+ * Generate a human-readable summary string from a PromotionResult.
133
+ *
134
+ * @param path - The promotion result to summarize
135
+ * @param options - Optional display settings
136
+ * @returns Formatted multi-line summary string
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const path = promotionPath(ruleset, facts, { tier: 'preferred' });
141
+ * console.log(summarizePromotionPath(path));
142
+ * ```
143
+ */
144
+ function summarizePromotionPath(path, options = {}) {
145
+ const { maxOpportunities } = options;
146
+ const lines = [];
147
+ const currentLabel = path.currentTier ? `${path.currentScore} (${path.currentTier})` : `${path.currentScore}`;
148
+ const targetLabel = path.targetTier ? `${path.targetScore} (${path.targetTier})` : `${path.targetScore}`;
149
+ lines.push(`Current: ${currentLabel}`);
150
+ lines.push(`Target: ${targetLabel}`);
151
+ lines.push(`Gap: ${path.gap} points`);
152
+ lines.push(`Recoverable: ${path.totalRecoverable} points from ${path.opportunities.length} opportunities`);
153
+ if (path.alreadyMet) {
154
+ lines.push('');
155
+ lines.push('Target already met.');
156
+ return lines.join('\n');
157
+ }
158
+ const opps = maxOpportunities != null
159
+ ? path.opportunities.slice(0, maxOpportunities)
160
+ : path.opportunities;
161
+ if (opps.length > 0) {
162
+ lines.push('');
163
+ for (const opp of opps) {
164
+ const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
165
+ lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
166
+ if (opp.reason)
167
+ lines.push(` ${opp.reason}`);
168
+ }
169
+ if (maxOpportunities != null && path.opportunities.length > maxOpportunities) {
170
+ const remaining = path.opportunities.length - maxOpportunities;
171
+ lines.push(` ... and ${remaining} more`);
172
+ }
173
+ }
174
+ return lines.join('\n');
175
+ }
176
+ /**
177
+ * Generate a human-readable category breakdown from a PromotionResult.
178
+ *
179
+ * Groups opportunities by category and shows per-category recoverable
180
+ * points with individual opportunity details.
181
+ *
182
+ * @param path - The promotion result to summarize
183
+ * @param options - Optional display settings
184
+ * @returns Formatted multi-line category breakdown string
185
+ *
186
+ * @example
187
+ * ```typescript
188
+ * const path = promotionPath(ruleset, facts, { tier: 'elite' });
189
+ * console.log(summarizeCategoryBreakdown(path));
190
+ * ```
191
+ */
192
+ function summarizeCategoryBreakdown(path, options = {}) {
193
+ const { maxPerCategory } = options;
194
+ const lines = [];
195
+ // Group by category
196
+ const byCategory = new Map();
197
+ for (const opp of path.opportunities) {
198
+ const cat = opp.category ?? '(uncategorized)';
199
+ if (!byCategory.has(cat))
200
+ byCategory.set(cat, []);
201
+ byCategory.get(cat).push(opp);
202
+ }
203
+ for (const [category, opps] of byCategory) {
204
+ const categoryTotal = opps.reduce((sum, o) => sum + (o.potentialScore ?? 0), 0);
205
+ lines.push(`${category} (${categoryTotal} recoverable points):`);
206
+ const displayed = maxPerCategory != null ? opps.slice(0, maxPerCategory) : opps;
207
+ for (const opp of displayed) {
208
+ const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
209
+ lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
210
+ if (opp.reason)
211
+ lines.push(` ${opp.reason}`);
212
+ }
213
+ if (maxPerCategory != null && opps.length > maxPerCategory) {
214
+ const remaining = opps.length - maxPerCategory;
215
+ lines.push(` ... and ${remaining} more`);
216
+ }
217
+ lines.push('');
218
+ }
219
+ // Remove trailing blank line
220
+ if (lines.length > 0 && lines[lines.length - 1] === '') {
221
+ lines.pop();
222
+ }
223
+ return lines.join('\n');
224
+ }
225
+ //# 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,46 @@
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
+ /** Options for summarizePromotionPath */
26
+ export type PromotionSummaryOptions = {
27
+ maxOpportunities?: number;
28
+ };
29
+ /** Options for summarizeCategoryBreakdown */
30
+ export type CategorySummaryOptions = {
31
+ maxPerCategory?: number;
32
+ };
33
+ /** Promotion path result */
34
+ export type PromotionResult<T extends TierDefinition = TierDefinition> = {
35
+ currentScore: number;
36
+ currentTier?: string;
37
+ targetScore: number;
38
+ targetTier?: string;
39
+ gap: number;
40
+ alreadyMet: boolean;
41
+ opportunities: PromotionOpportunity[];
42
+ totalRecoverable: number;
43
+ result: ScoringResult<T>;
44
+ executionTimeMs: number;
45
+ };
46
+ //# 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,yCAAyC;AACzC,MAAM,MAAM,uBAAuB,GAAG;IACpC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,6CAA6C;AAC7C,MAAM,MAAM,sBAAsB,GAAG;IACnC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF,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.16",
3
+ "version": "1.0.18",
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",
package/src/index.ts CHANGED
@@ -418,3 +418,9 @@ export * from './abtesting';
418
418
  // ========================================
419
419
 
420
420
  export * from './compare';
421
+
422
+ // ========================================
423
+ // Promotion — Counterfactual Scoring Analysis
424
+ // ========================================
425
+
426
+ export * from './promotion';
@@ -0,0 +1,5 @@
1
+ // Types
2
+ export type { PromotionOpportunity, PromotionTarget, PromotionResult, PromotionSummaryOptions, CategorySummaryOptions } from './types';
3
+
4
+ // Core promotion path
5
+ export { promotionPath, summarizePromotionPath, summarizeCategoryBreakdown } from './promotion';
@@ -0,0 +1,260 @@
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, PromotionSummaryOptions, CategorySummaryOptions } 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(vendorRuleset, vendorFacts, { tier: 'preferred' });
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
+ }
148
+
149
+ /**
150
+ * Generate a human-readable summary string from a PromotionResult.
151
+ *
152
+ * @param path - The promotion result to summarize
153
+ * @param options - Optional display settings
154
+ * @returns Formatted multi-line summary string
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const path = promotionPath(ruleset, facts, { tier: 'preferred' });
159
+ * console.log(summarizePromotionPath(path));
160
+ * ```
161
+ */
162
+ export function summarizePromotionPath(
163
+ path: PromotionResult,
164
+ options: PromotionSummaryOptions = {}
165
+ ): string {
166
+ const { maxOpportunities } = options;
167
+ const lines: string[] = [];
168
+
169
+ const currentLabel = path.currentTier ? `${path.currentScore} (${path.currentTier})` : `${path.currentScore}`;
170
+ const targetLabel = path.targetTier ? `${path.targetScore} (${path.targetTier})` : `${path.targetScore}`;
171
+
172
+ lines.push(`Current: ${currentLabel}`);
173
+ lines.push(`Target: ${targetLabel}`);
174
+ lines.push(`Gap: ${path.gap} points`);
175
+ lines.push(`Recoverable: ${path.totalRecoverable} points from ${path.opportunities.length} opportunities`);
176
+
177
+ if (path.alreadyMet) {
178
+ lines.push('');
179
+ lines.push('Target already met.');
180
+ return lines.join('\n');
181
+ }
182
+
183
+ const opps = maxOpportunities != null
184
+ ? path.opportunities.slice(0, maxOpportunities)
185
+ : path.opportunities;
186
+
187
+ if (opps.length > 0) {
188
+ lines.push('');
189
+ for (const opp of opps) {
190
+ const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
191
+ lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
192
+ if (opp.reason) lines.push(` ${opp.reason}`);
193
+ }
194
+
195
+ if (maxOpportunities != null && path.opportunities.length > maxOpportunities) {
196
+ const remaining = path.opportunities.length - maxOpportunities;
197
+ lines.push(` ... and ${remaining} more`);
198
+ }
199
+ }
200
+
201
+ return lines.join('\n');
202
+ }
203
+
204
+ /**
205
+ * Generate a human-readable category breakdown from a PromotionResult.
206
+ *
207
+ * Groups opportunities by category and shows per-category recoverable
208
+ * points with individual opportunity details.
209
+ *
210
+ * @param path - The promotion result to summarize
211
+ * @param options - Optional display settings
212
+ * @returns Formatted multi-line category breakdown string
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * const path = promotionPath(ruleset, facts, { tier: 'elite' });
217
+ * console.log(summarizeCategoryBreakdown(path));
218
+ * ```
219
+ */
220
+ export function summarizeCategoryBreakdown(
221
+ path: PromotionResult,
222
+ options: CategorySummaryOptions = {}
223
+ ): string {
224
+ const { maxPerCategory } = options;
225
+ const lines: string[] = [];
226
+
227
+ // Group by category
228
+ const byCategory = new Map<string, PromotionOpportunity[]>();
229
+ for (const opp of path.opportunities) {
230
+ const cat = opp.category ?? '(uncategorized)';
231
+ if (!byCategory.has(cat)) byCategory.set(cat, []);
232
+ byCategory.get(cat)!.push(opp);
233
+ }
234
+
235
+ for (const [category, opps] of byCategory) {
236
+ const categoryTotal = opps.reduce((sum, o) => sum + (o.potentialScore ?? 0), 0);
237
+ lines.push(`${category} (${categoryTotal} recoverable points):`);
238
+
239
+ const displayed = maxPerCategory != null ? opps.slice(0, maxPerCategory) : opps;
240
+ for (const opp of displayed) {
241
+ const impact = opp.dynamic ? 'variable' : `+${opp.potentialScore}`;
242
+ lines.push(` [${impact}] ${opp.ruleName ?? opp.ruleId}`);
243
+ if (opp.reason) lines.push(` ${opp.reason}`);
244
+ }
245
+
246
+ if (maxPerCategory != null && opps.length > maxPerCategory) {
247
+ const remaining = opps.length - maxPerCategory;
248
+ lines.push(` ... and ${remaining} more`);
249
+ }
250
+
251
+ lines.push('');
252
+ }
253
+
254
+ // Remove trailing blank line
255
+ if (lines.length > 0 && lines[lines.length - 1] === '') {
256
+ lines.pop();
257
+ }
258
+
259
+ return lines.join('\n');
260
+ }
@@ -0,0 +1,47 @@
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
+ /** Options for summarizePromotionPath */
26
+ export type PromotionSummaryOptions = {
27
+ maxOpportunities?: number; // max opportunities to display (default: all)
28
+ };
29
+
30
+ /** Options for summarizeCategoryBreakdown */
31
+ export type CategorySummaryOptions = {
32
+ maxPerCategory?: number; // max opportunities per category (default: all)
33
+ };
34
+
35
+ /** Promotion path result */
36
+ export type PromotionResult<T extends TierDefinition = TierDefinition> = {
37
+ currentScore: number;
38
+ currentTier?: string;
39
+ targetScore: number; // resolved threshold of target tier, or explicit score
40
+ targetTier?: string;
41
+ gap: number; // targetScore - currentScore (0 if already met)
42
+ alreadyMet: boolean; // true if current score/tier already meets target
43
+ opportunities: PromotionOpportunity[]; // ranked by potentialScore desc (nulls last)
44
+ totalRecoverable: number; // sum of all static potentialScores
45
+ result: ScoringResult<T>; // the full scoring result for reference
46
+ executionTimeMs: number;
47
+ };