agentic-qe 3.7.21 → 3.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/helpers/brain-checkpoint.cjs +4 -1
- package/.claude/helpers/statusline-v3.cjs +3 -1
- package/.claude/skills/skills-manifest.json +1 -1
- package/CHANGELOG.md +45 -0
- package/README.md +2 -14
- package/assets/helpers/statusline-v3.cjs +3 -1
- package/dist/cli/brain-commands.js +6 -10
- package/dist/cli/bundle.js +7441 -4327
- package/dist/cli/commands/audit.d.ts +43 -0
- package/dist/cli/commands/audit.js +125 -0
- package/dist/cli/commands/hooks.js +29 -6
- package/dist/cli/commands/init.js +1 -73
- package/dist/cli/commands/learning.js +270 -13
- package/dist/cli/commands/ruvector-commands.d.ts +15 -0
- package/dist/cli/commands/ruvector-commands.js +271 -0
- package/dist/cli/handlers/init-handler.d.ts +0 -1
- package/dist/cli/handlers/init-handler.js +0 -6
- package/dist/cli/index.js +4 -2
- package/dist/context/sources/defect-source.js +2 -2
- package/dist/context/sources/memory-source.js +2 -2
- package/dist/context/sources/requirements-source.js +2 -2
- package/dist/coordination/behavior-tree/decorators.d.ts +108 -0
- package/dist/coordination/behavior-tree/decorators.js +251 -0
- package/dist/coordination/behavior-tree/index.d.ts +12 -0
- package/dist/coordination/behavior-tree/index.js +15 -0
- package/dist/coordination/behavior-tree/nodes.d.ts +165 -0
- package/dist/coordination/behavior-tree/nodes.js +338 -0
- package/dist/coordination/behavior-tree/qe-trees.d.ts +105 -0
- package/dist/coordination/behavior-tree/qe-trees.js +181 -0
- package/dist/coordination/coherence-action-gate.d.ts +284 -0
- package/dist/coordination/coherence-action-gate.js +512 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.js +8 -0
- package/dist/coordination/reasoning-qec.d.ts +315 -0
- package/dist/coordination/reasoning-qec.js +585 -0
- package/dist/coordination/task-executor.d.ts +16 -0
- package/dist/coordination/task-executor.js +99 -0
- package/dist/coordination/workflow-orchestrator.d.ts +29 -0
- package/dist/coordination/workflow-orchestrator.js +42 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.d.ts +135 -0
- package/dist/domains/visual-accessibility/cnn-visual-regression.js +327 -0
- package/dist/domains/visual-accessibility/index.d.ts +1 -0
- package/dist/domains/visual-accessibility/index.js +4 -0
- package/dist/governance/coherence-validator.d.ts +112 -0
- package/dist/governance/coherence-validator.js +180 -0
- package/dist/governance/index.d.ts +1 -0
- package/dist/governance/index.js +2 -0
- package/dist/governance/witness-chain.d.ts +311 -0
- package/dist/governance/witness-chain.js +509 -0
- package/dist/init/index.d.ts +0 -2
- package/dist/init/index.js +0 -1
- package/dist/init/init-wizard-steps.d.ts +10 -0
- package/dist/init/init-wizard-steps.js +87 -1
- package/dist/init/init-wizard.d.ts +1 -9
- package/dist/init/init-wizard.js +3 -69
- package/dist/init/orchestrator.js +0 -1
- package/dist/init/phases/01-detection.js +0 -27
- package/dist/init/phases/07-hooks.js +6 -4
- package/dist/init/phases/phase-interface.d.ts +0 -1
- package/dist/init/settings-merge.js +1 -1
- package/dist/integrations/browser/qe-dashboard/clustering.d.ts +48 -0
- package/dist/integrations/browser/qe-dashboard/clustering.js +183 -0
- package/dist/integrations/browser/qe-dashboard/index.d.ts +12 -0
- package/dist/integrations/browser/qe-dashboard/index.js +15 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.d.ts +165 -0
- package/dist/integrations/browser/qe-dashboard/pattern-explorer.js +260 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.d.ts +144 -0
- package/dist/integrations/browser/qe-dashboard/wasm-vector-store.js +277 -0
- package/dist/integrations/ruvector/cognitive-container-codec.d.ts +51 -0
- package/dist/integrations/ruvector/cognitive-container-codec.js +180 -0
- package/dist/integrations/ruvector/cognitive-container.d.ts +125 -0
- package/dist/integrations/ruvector/cognitive-container.js +306 -0
- package/dist/integrations/ruvector/coherence-gate.d.ts +309 -0
- package/dist/integrations/ruvector/coherence-gate.js +631 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.d.ts +176 -0
- package/dist/integrations/ruvector/compressed-hnsw-integration.js +301 -0
- package/dist/integrations/ruvector/dither-adapter.d.ts +122 -0
- package/dist/integrations/ruvector/dither-adapter.js +295 -0
- package/dist/integrations/ruvector/domain-transfer.d.ts +129 -0
- package/dist/integrations/ruvector/domain-transfer.js +220 -0
- package/dist/integrations/ruvector/feature-flags.d.ts +214 -2
- package/dist/integrations/ruvector/feature-flags.js +167 -2
- package/dist/integrations/ruvector/filter-adapter.d.ts +71 -0
- package/dist/integrations/ruvector/filter-adapter.js +285 -0
- package/dist/integrations/ruvector/gnn-wrapper.d.ts +20 -0
- package/dist/integrations/ruvector/gnn-wrapper.js +40 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.d.ts +237 -0
- package/dist/integrations/ruvector/hnsw-health-monitor.js +394 -0
- package/dist/integrations/ruvector/index.d.ts +8 -2
- package/dist/integrations/ruvector/index.js +18 -2
- package/dist/integrations/ruvector/interfaces.d.ts +40 -0
- package/dist/integrations/ruvector/sona-persistence.d.ts +54 -0
- package/dist/integrations/ruvector/sona-persistence.js +162 -0
- package/dist/integrations/ruvector/sona-three-loop.d.ts +392 -0
- package/dist/integrations/ruvector/sona-three-loop.js +814 -0
- package/dist/integrations/ruvector/sona-wrapper.d.ts +97 -0
- package/dist/integrations/ruvector/sona-wrapper.js +147 -3
- package/dist/integrations/ruvector/spectral-math.d.ts +101 -0
- package/dist/integrations/ruvector/spectral-math.js +254 -0
- package/dist/integrations/ruvector/temporal-compression.d.ts +163 -0
- package/dist/integrations/ruvector/temporal-compression.js +318 -0
- package/dist/integrations/ruvector/thompson-sampler.d.ts +61 -0
- package/dist/integrations/ruvector/thompson-sampler.js +118 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.d.ts +80 -0
- package/dist/integrations/ruvector/transfer-coherence-stub.js +63 -0
- package/dist/integrations/ruvector/transfer-verification.d.ts +119 -0
- package/dist/integrations/ruvector/transfer-verification.js +115 -0
- package/dist/kernel/hnsw-adapter.d.ts +52 -1
- package/dist/kernel/hnsw-adapter.js +139 -4
- package/dist/kernel/hnsw-index-provider.d.ts +5 -0
- package/dist/kernel/native-hnsw-backend.d.ts +110 -0
- package/dist/kernel/native-hnsw-backend.js +408 -0
- package/dist/kernel/unified-memory.js +5 -6
- package/dist/learning/aqe-learning-engine.d.ts +2 -0
- package/dist/learning/aqe-learning-engine.js +65 -0
- package/dist/learning/experience-capture-middleware.js +20 -0
- package/dist/learning/experience-capture.d.ts +10 -0
- package/dist/learning/experience-capture.js +34 -0
- package/dist/learning/index.d.ts +2 -2
- package/dist/learning/index.js +4 -4
- package/dist/learning/metrics-tracker.d.ts +11 -0
- package/dist/learning/metrics-tracker.js +29 -13
- package/dist/learning/pattern-lifecycle.d.ts +30 -1
- package/dist/learning/pattern-lifecycle.js +92 -20
- package/dist/learning/pattern-store.d.ts +8 -0
- package/dist/learning/pattern-store.js +8 -2
- package/dist/learning/qe-unified-memory.js +1 -28
- package/dist/learning/regret-tracker.d.ts +201 -0
- package/dist/learning/regret-tracker.js +361 -0
- package/dist/mcp/bundle.js +5915 -474
- package/dist/routing/index.d.ts +4 -2
- package/dist/routing/index.js +3 -1
- package/dist/routing/neural-tiny-dancer-router.d.ts +268 -0
- package/dist/routing/neural-tiny-dancer-router.js +514 -0
- package/dist/routing/queen-integration.js +5 -5
- package/dist/routing/routing-config.d.ts +6 -0
- package/dist/routing/routing-config.js +1 -0
- package/dist/routing/simple-neural-router.d.ts +76 -0
- package/dist/routing/simple-neural-router.js +202 -0
- package/dist/routing/tiny-dancer-router.d.ts +20 -1
- package/dist/routing/tiny-dancer-router.js +21 -2
- package/dist/test-scheduling/dag-attention-scheduler.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-scheduler.js +358 -0
- package/dist/test-scheduling/dag-attention-types.d.ts +81 -0
- package/dist/test-scheduling/dag-attention-types.js +10 -0
- package/dist/test-scheduling/index.d.ts +1 -0
- package/dist/test-scheduling/index.js +4 -0
- package/dist/test-scheduling/pipeline.d.ts +8 -0
- package/dist/test-scheduling/pipeline.js +28 -0
- package/package.json +6 -2
- package/dist/cli/commands/migrate.d.ts +0 -9
- package/dist/cli/commands/migrate.js +0 -566
- package/dist/init/init-wizard-migration.d.ts +0 -52
- package/dist/init/init-wizard-migration.js +0 -345
- package/dist/init/migration/config-migrator.d.ts +0 -31
- package/dist/init/migration/config-migrator.js +0 -149
- package/dist/init/migration/data-migrator.d.ts +0 -72
- package/dist/init/migration/data-migrator.js +0 -232
- package/dist/init/migration/detector.d.ts +0 -44
- package/dist/init/migration/detector.js +0 -105
- package/dist/init/migration/index.d.ts +0 -8
- package/dist/init/migration/index.js +0 -8
- package/dist/learning/v2-to-v3-migration.d.ts +0 -86
- package/dist/learning/v2-to-v3-migration.js +0 -529
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Regret Tracker
|
|
3
|
+
* Task 2.4: Regret Tracking and Learning Health Dashboard
|
|
4
|
+
*
|
|
5
|
+
* Tracks cumulative regret per domain over time to determine whether
|
|
6
|
+
* QE agents are actually learning. Uses log-log regression on the
|
|
7
|
+
* regret curve to classify growth rate:
|
|
8
|
+
*
|
|
9
|
+
* - Sublinear: R(n) ~ n^alpha where alpha < 1 (learning is happening)
|
|
10
|
+
* - Linear: R(n) ~ n (stagnation, needs intervention)
|
|
11
|
+
* - Superlinear: R(n) ~ n^alpha where alpha > 1 (getting worse)
|
|
12
|
+
*
|
|
13
|
+
* @module learning/regret-tracker
|
|
14
|
+
*/
|
|
15
|
+
/** A single point on the regret curve */
|
|
16
|
+
export interface RegretPoint {
|
|
17
|
+
/** Number of decisions made so far */
|
|
18
|
+
decisionCount: number;
|
|
19
|
+
/** Cumulative regret at this point */
|
|
20
|
+
cumulativeRegret: number;
|
|
21
|
+
/** Timestamp when this decision was recorded */
|
|
22
|
+
timestamp: number;
|
|
23
|
+
}
|
|
24
|
+
/** Growth rate classification for a domain's regret curve */
|
|
25
|
+
export type GrowthRate = 'sublinear' | 'linear' | 'superlinear' | 'insufficient_data';
|
|
26
|
+
/** Health summary for a single domain */
|
|
27
|
+
export interface DomainHealthSummary {
|
|
28
|
+
/** Domain identifier */
|
|
29
|
+
domain: string;
|
|
30
|
+
/** Total number of decisions recorded */
|
|
31
|
+
totalDecisions: number;
|
|
32
|
+
/** Current cumulative regret */
|
|
33
|
+
cumulativeRegret: number;
|
|
34
|
+
/** Growth rate classification */
|
|
35
|
+
growthRate: GrowthRate;
|
|
36
|
+
/** Whether stagnation has been detected */
|
|
37
|
+
stagnating: boolean;
|
|
38
|
+
/** Log-log slope (undefined if insufficient data) */
|
|
39
|
+
slope: number | undefined;
|
|
40
|
+
/** Average regret per decision (recent window) */
|
|
41
|
+
recentAvgRegret: number;
|
|
42
|
+
}
|
|
43
|
+
/** Alert emitted when a domain's regret growth rate changes */
|
|
44
|
+
export interface RegretAlert {
|
|
45
|
+
/** Domain that triggered the alert */
|
|
46
|
+
domain: string;
|
|
47
|
+
/** Previous growth rate */
|
|
48
|
+
previousRate: GrowthRate;
|
|
49
|
+
/** New growth rate */
|
|
50
|
+
newRate: GrowthRate;
|
|
51
|
+
/** Timestamp of the alert */
|
|
52
|
+
timestamp: number;
|
|
53
|
+
/** Human-readable message */
|
|
54
|
+
message: string;
|
|
55
|
+
}
|
|
56
|
+
/** Callback for regret alerts */
|
|
57
|
+
export type RegretAlertCallback = (alert: RegretAlert) => void;
|
|
58
|
+
/**
|
|
59
|
+
* Compute the slope of a simple linear regression (y = mx + b).
|
|
60
|
+
*
|
|
61
|
+
* Uses the ordinary least squares formula:
|
|
62
|
+
* m = (n * sum(xy) - sum(x) * sum(y)) / (n * sum(x^2) - (sum(x))^2)
|
|
63
|
+
*
|
|
64
|
+
* @param xs - Independent variable values
|
|
65
|
+
* @param ys - Dependent variable values
|
|
66
|
+
* @returns Slope of the regression line
|
|
67
|
+
*/
|
|
68
|
+
export declare function linearRegressionSlope(xs: number[], ys: number[]): number;
|
|
69
|
+
/**
|
|
70
|
+
* Tracks cumulative regret per domain to assess whether QE agents
|
|
71
|
+
* are learning over time. Regret_i = optimal_reward - actual_reward.
|
|
72
|
+
* R(n) = sum of regret_i for i=1..n. Sublinear R(n) ~ n^alpha (alpha < 1)
|
|
73
|
+
* indicates learning; linear or superlinear indicates stagnation.
|
|
74
|
+
*/
|
|
75
|
+
export declare class RegretTracker {
|
|
76
|
+
/** Per-domain regret tracking state */
|
|
77
|
+
private domains;
|
|
78
|
+
/** Alert callbacks */
|
|
79
|
+
private alertCallbacks;
|
|
80
|
+
/** Historical alerts for review */
|
|
81
|
+
private alerts;
|
|
82
|
+
/** Maximum number of alerts to retain */
|
|
83
|
+
private readonly maxAlerts;
|
|
84
|
+
/** Window size for recent average regret calculation */
|
|
85
|
+
private readonly recentWindow;
|
|
86
|
+
constructor(options?: {
|
|
87
|
+
maxAlerts?: number;
|
|
88
|
+
recentWindow?: number;
|
|
89
|
+
});
|
|
90
|
+
/**
|
|
91
|
+
* Record a decision outcome for regret tracking.
|
|
92
|
+
*
|
|
93
|
+
* @param domain - The QE domain (e.g. 'test-generation', 'coverage-analysis')
|
|
94
|
+
* @param reward - The actual reward received (0 to 1)
|
|
95
|
+
* @param optimalReward - The best possible reward (0 to 1)
|
|
96
|
+
*/
|
|
97
|
+
recordDecision(domain: string, reward: number, optimalReward: number): void;
|
|
98
|
+
/**
|
|
99
|
+
* Get the current cumulative regret for a domain.
|
|
100
|
+
*
|
|
101
|
+
* @param domain - Domain identifier
|
|
102
|
+
* @returns Cumulative regret, or 0 if domain not tracked
|
|
103
|
+
*/
|
|
104
|
+
getCumulativeRegret(domain: string): number;
|
|
105
|
+
/**
|
|
106
|
+
* Get the full regret curve for a domain.
|
|
107
|
+
*
|
|
108
|
+
* @param domain - Domain identifier
|
|
109
|
+
* @returns Array of regret data points, or empty array if not tracked
|
|
110
|
+
*/
|
|
111
|
+
getRegretCurve(domain: string): RegretPoint[];
|
|
112
|
+
/**
|
|
113
|
+
* Classify the growth rate of cumulative regret for a domain.
|
|
114
|
+
*
|
|
115
|
+
* Uses log-log linear regression: if R(n) ~ n^alpha, then
|
|
116
|
+
* log(R) ~ alpha * log(n), so the slope of log(R) vs log(n)
|
|
117
|
+
* gives us alpha.
|
|
118
|
+
*
|
|
119
|
+
* @param domain - Domain identifier
|
|
120
|
+
* @returns Growth rate classification
|
|
121
|
+
*/
|
|
122
|
+
getRegretGrowthRate(domain: string): GrowthRate;
|
|
123
|
+
/**
|
|
124
|
+
* Detect whether a domain is stagnating (linear or superlinear regret growth).
|
|
125
|
+
*
|
|
126
|
+
* @param domain - Domain identifier
|
|
127
|
+
* @returns true if the domain shows stagnation
|
|
128
|
+
*/
|
|
129
|
+
detectStagnation(domain: string): boolean;
|
|
130
|
+
/**
|
|
131
|
+
* Get a health summary for all tracked domains.
|
|
132
|
+
*
|
|
133
|
+
* @returns Array of domain health summaries, sorted by domain name
|
|
134
|
+
*/
|
|
135
|
+
getHealthSummary(): DomainHealthSummary[];
|
|
136
|
+
/**
|
|
137
|
+
* Register a callback for regret growth rate transition alerts.
|
|
138
|
+
*
|
|
139
|
+
* @param callback - Function to call when a growth rate transition occurs
|
|
140
|
+
*/
|
|
141
|
+
onAlert(callback: RegretAlertCallback): void;
|
|
142
|
+
/**
|
|
143
|
+
* Get all historical alerts.
|
|
144
|
+
*
|
|
145
|
+
* @returns Array of past alerts
|
|
146
|
+
*/
|
|
147
|
+
getAlerts(): RegretAlert[];
|
|
148
|
+
/**
|
|
149
|
+
* Get list of all tracked domains.
|
|
150
|
+
*/
|
|
151
|
+
getTrackedDomains(): string[];
|
|
152
|
+
/**
|
|
153
|
+
* Get the total number of decisions across all domains.
|
|
154
|
+
*/
|
|
155
|
+
getTotalDecisions(): number;
|
|
156
|
+
/**
|
|
157
|
+
* Reset tracking data for a specific domain (or all domains).
|
|
158
|
+
*
|
|
159
|
+
* @param domain - Domain to reset, or undefined to reset all
|
|
160
|
+
*/
|
|
161
|
+
reset(domain?: string): void;
|
|
162
|
+
/**
|
|
163
|
+
* Get or create domain tracking state.
|
|
164
|
+
*/
|
|
165
|
+
private getOrCreateDomainState;
|
|
166
|
+
/**
|
|
167
|
+
* Classify the growth rate of a regret curve using log-log regression.
|
|
168
|
+
*
|
|
169
|
+
* @param points - Regret data points
|
|
170
|
+
* @returns Growth rate classification
|
|
171
|
+
*/
|
|
172
|
+
private classifyGrowthRate;
|
|
173
|
+
/**
|
|
174
|
+
* Compute the slope of log(regret) vs log(n) for the regret curve.
|
|
175
|
+
*
|
|
176
|
+
* Filters out points where cumulative regret is zero (log(0) is undefined).
|
|
177
|
+
*
|
|
178
|
+
* @param points - Regret data points
|
|
179
|
+
* @returns Slope value, or undefined if insufficient valid data
|
|
180
|
+
*/
|
|
181
|
+
private computeLogLogSlope;
|
|
182
|
+
/**
|
|
183
|
+
* Compute the average per-decision regret over the recent window.
|
|
184
|
+
*/
|
|
185
|
+
private computeRecentAvgRegret;
|
|
186
|
+
/**
|
|
187
|
+
* Emit a growth rate transition alert.
|
|
188
|
+
*/
|
|
189
|
+
private emitAlert;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Create a new RegretTracker instance.
|
|
193
|
+
*
|
|
194
|
+
* @param options - Optional configuration
|
|
195
|
+
* @returns New RegretTracker
|
|
196
|
+
*/
|
|
197
|
+
export declare function createRegretTracker(options?: {
|
|
198
|
+
maxAlerts?: number;
|
|
199
|
+
recentWindow?: number;
|
|
200
|
+
}): RegretTracker;
|
|
201
|
+
//# sourceMappingURL=regret-tracker.d.ts.map
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentic QE v3 - Regret Tracker
|
|
3
|
+
* Task 2.4: Regret Tracking and Learning Health Dashboard
|
|
4
|
+
*
|
|
5
|
+
* Tracks cumulative regret per domain over time to determine whether
|
|
6
|
+
* QE agents are actually learning. Uses log-log regression on the
|
|
7
|
+
* regret curve to classify growth rate:
|
|
8
|
+
*
|
|
9
|
+
* - Sublinear: R(n) ~ n^alpha where alpha < 1 (learning is happening)
|
|
10
|
+
* - Linear: R(n) ~ n (stagnation, needs intervention)
|
|
11
|
+
* - Superlinear: R(n) ~ n^alpha where alpha > 1 (getting worse)
|
|
12
|
+
*
|
|
13
|
+
* @module learning/regret-tracker
|
|
14
|
+
*/
|
|
15
|
+
import { LoggerFactory } from '../logging/index.js';
|
|
16
|
+
const logger = LoggerFactory.create('regret-tracker');
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Constants
|
|
19
|
+
// ============================================================================
|
|
20
|
+
/** Minimum data points required for reliable growth rate classification */
|
|
21
|
+
const MIN_DATA_POINTS_FOR_CLASSIFICATION = 50;
|
|
22
|
+
/** Slope threshold below which growth is classified as sublinear */
|
|
23
|
+
const SUBLINEAR_THRESHOLD = 0.9;
|
|
24
|
+
/** Slope threshold above which growth is classified as superlinear */
|
|
25
|
+
const SUPERLINEAR_THRESHOLD = 1.1;
|
|
26
|
+
// ============================================================================
|
|
27
|
+
// Linear Regression Helper
|
|
28
|
+
// ============================================================================
|
|
29
|
+
/**
|
|
30
|
+
* Compute the slope of a simple linear regression (y = mx + b).
|
|
31
|
+
*
|
|
32
|
+
* Uses the ordinary least squares formula:
|
|
33
|
+
* m = (n * sum(xy) - sum(x) * sum(y)) / (n * sum(x^2) - (sum(x))^2)
|
|
34
|
+
*
|
|
35
|
+
* @param xs - Independent variable values
|
|
36
|
+
* @param ys - Dependent variable values
|
|
37
|
+
* @returns Slope of the regression line
|
|
38
|
+
*/
|
|
39
|
+
export function linearRegressionSlope(xs, ys) {
|
|
40
|
+
const n = xs.length;
|
|
41
|
+
if (n < 2)
|
|
42
|
+
return 0;
|
|
43
|
+
let sumX = 0;
|
|
44
|
+
let sumY = 0;
|
|
45
|
+
let sumXY = 0;
|
|
46
|
+
let sumX2 = 0;
|
|
47
|
+
for (let i = 0; i < n; i++) {
|
|
48
|
+
sumX += xs[i];
|
|
49
|
+
sumY += ys[i];
|
|
50
|
+
sumXY += xs[i] * ys[i];
|
|
51
|
+
sumX2 += xs[i] * xs[i];
|
|
52
|
+
}
|
|
53
|
+
const denominator = n * sumX2 - sumX * sumX;
|
|
54
|
+
if (Math.abs(denominator) < 1e-10)
|
|
55
|
+
return 0;
|
|
56
|
+
return (n * sumXY - sumX * sumY) / denominator;
|
|
57
|
+
}
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// RegretTracker
|
|
60
|
+
// ============================================================================
|
|
61
|
+
/**
|
|
62
|
+
* Tracks cumulative regret per domain to assess whether QE agents
|
|
63
|
+
* are learning over time. Regret_i = optimal_reward - actual_reward.
|
|
64
|
+
* R(n) = sum of regret_i for i=1..n. Sublinear R(n) ~ n^alpha (alpha < 1)
|
|
65
|
+
* indicates learning; linear or superlinear indicates stagnation.
|
|
66
|
+
*/
|
|
67
|
+
export class RegretTracker {
|
|
68
|
+
/** Per-domain regret tracking state */
|
|
69
|
+
domains = new Map();
|
|
70
|
+
/** Alert callbacks */
|
|
71
|
+
alertCallbacks = [];
|
|
72
|
+
/** Historical alerts for review */
|
|
73
|
+
alerts = [];
|
|
74
|
+
/** Maximum number of alerts to retain */
|
|
75
|
+
maxAlerts;
|
|
76
|
+
/** Window size for recent average regret calculation */
|
|
77
|
+
recentWindow;
|
|
78
|
+
constructor(options) {
|
|
79
|
+
this.maxAlerts = options?.maxAlerts ?? 100;
|
|
80
|
+
this.recentWindow = options?.recentWindow ?? 20;
|
|
81
|
+
}
|
|
82
|
+
// ==========================================================================
|
|
83
|
+
// Core Recording
|
|
84
|
+
// ==========================================================================
|
|
85
|
+
/**
|
|
86
|
+
* Record a decision outcome for regret tracking.
|
|
87
|
+
*
|
|
88
|
+
* @param domain - The QE domain (e.g. 'test-generation', 'coverage-analysis')
|
|
89
|
+
* @param reward - The actual reward received (0 to 1)
|
|
90
|
+
* @param optimalReward - The best possible reward (0 to 1)
|
|
91
|
+
*/
|
|
92
|
+
recordDecision(domain, reward, optimalReward) {
|
|
93
|
+
const state = this.getOrCreateDomainState(domain);
|
|
94
|
+
const regret = Math.max(0, optimalReward - reward);
|
|
95
|
+
state.cumulativeRegret += regret;
|
|
96
|
+
state.decisionCount++;
|
|
97
|
+
state.points.push({
|
|
98
|
+
decisionCount: state.decisionCount,
|
|
99
|
+
cumulativeRegret: state.cumulativeRegret,
|
|
100
|
+
timestamp: Date.now(),
|
|
101
|
+
});
|
|
102
|
+
// Check for growth rate transitions after enough data
|
|
103
|
+
if (state.decisionCount >= MIN_DATA_POINTS_FOR_CLASSIFICATION) {
|
|
104
|
+
const currentRate = this.classifyGrowthRate(state.points);
|
|
105
|
+
if (state.lastGrowthRate !== 'insufficient_data' &&
|
|
106
|
+
currentRate !== state.lastGrowthRate) {
|
|
107
|
+
this.emitAlert(domain, state.lastGrowthRate, currentRate);
|
|
108
|
+
}
|
|
109
|
+
state.lastGrowthRate = currentRate;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// ==========================================================================
|
|
113
|
+
// Regret Queries
|
|
114
|
+
// ==========================================================================
|
|
115
|
+
/**
|
|
116
|
+
* Get the current cumulative regret for a domain.
|
|
117
|
+
*
|
|
118
|
+
* @param domain - Domain identifier
|
|
119
|
+
* @returns Cumulative regret, or 0 if domain not tracked
|
|
120
|
+
*/
|
|
121
|
+
getCumulativeRegret(domain) {
|
|
122
|
+
const state = this.domains.get(domain);
|
|
123
|
+
return state?.cumulativeRegret ?? 0;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Get the full regret curve for a domain.
|
|
127
|
+
*
|
|
128
|
+
* @param domain - Domain identifier
|
|
129
|
+
* @returns Array of regret data points, or empty array if not tracked
|
|
130
|
+
*/
|
|
131
|
+
getRegretCurve(domain) {
|
|
132
|
+
const state = this.domains.get(domain);
|
|
133
|
+
if (!state)
|
|
134
|
+
return [];
|
|
135
|
+
return [...state.points];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Classify the growth rate of cumulative regret for a domain.
|
|
139
|
+
*
|
|
140
|
+
* Uses log-log linear regression: if R(n) ~ n^alpha, then
|
|
141
|
+
* log(R) ~ alpha * log(n), so the slope of log(R) vs log(n)
|
|
142
|
+
* gives us alpha.
|
|
143
|
+
*
|
|
144
|
+
* @param domain - Domain identifier
|
|
145
|
+
* @returns Growth rate classification
|
|
146
|
+
*/
|
|
147
|
+
getRegretGrowthRate(domain) {
|
|
148
|
+
const state = this.domains.get(domain);
|
|
149
|
+
if (!state)
|
|
150
|
+
return 'insufficient_data';
|
|
151
|
+
return this.classifyGrowthRate(state.points);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Detect whether a domain is stagnating (linear or superlinear regret growth).
|
|
155
|
+
*
|
|
156
|
+
* @param domain - Domain identifier
|
|
157
|
+
* @returns true if the domain shows stagnation
|
|
158
|
+
*/
|
|
159
|
+
detectStagnation(domain) {
|
|
160
|
+
const rate = this.getRegretGrowthRate(domain);
|
|
161
|
+
return rate === 'linear' || rate === 'superlinear';
|
|
162
|
+
}
|
|
163
|
+
// ==========================================================================
|
|
164
|
+
// Health Dashboard
|
|
165
|
+
// ==========================================================================
|
|
166
|
+
/**
|
|
167
|
+
* Get a health summary for all tracked domains.
|
|
168
|
+
*
|
|
169
|
+
* @returns Array of domain health summaries, sorted by domain name
|
|
170
|
+
*/
|
|
171
|
+
getHealthSummary() {
|
|
172
|
+
const summaries = [];
|
|
173
|
+
for (const [domain, state] of this.domains) {
|
|
174
|
+
const growthRate = this.classifyGrowthRate(state.points);
|
|
175
|
+
const slope = this.computeLogLogSlope(state.points);
|
|
176
|
+
// Compute recent average regret
|
|
177
|
+
const recentAvgRegret = this.computeRecentAvgRegret(state);
|
|
178
|
+
summaries.push({
|
|
179
|
+
domain,
|
|
180
|
+
totalDecisions: state.decisionCount,
|
|
181
|
+
cumulativeRegret: state.cumulativeRegret,
|
|
182
|
+
growthRate,
|
|
183
|
+
stagnating: growthRate === 'linear' || growthRate === 'superlinear',
|
|
184
|
+
slope,
|
|
185
|
+
recentAvgRegret,
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
// Sort alphabetically by domain
|
|
189
|
+
summaries.sort((a, b) => a.domain.localeCompare(b.domain));
|
|
190
|
+
return summaries;
|
|
191
|
+
}
|
|
192
|
+
// ==========================================================================
|
|
193
|
+
// Alert System
|
|
194
|
+
// ==========================================================================
|
|
195
|
+
/**
|
|
196
|
+
* Register a callback for regret growth rate transition alerts.
|
|
197
|
+
*
|
|
198
|
+
* @param callback - Function to call when a growth rate transition occurs
|
|
199
|
+
*/
|
|
200
|
+
onAlert(callback) {
|
|
201
|
+
this.alertCallbacks.push(callback);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Get all historical alerts.
|
|
205
|
+
*
|
|
206
|
+
* @returns Array of past alerts
|
|
207
|
+
*/
|
|
208
|
+
getAlerts() {
|
|
209
|
+
return [...this.alerts];
|
|
210
|
+
}
|
|
211
|
+
// ==========================================================================
|
|
212
|
+
// Accessors
|
|
213
|
+
// ==========================================================================
|
|
214
|
+
/**
|
|
215
|
+
* Get list of all tracked domains.
|
|
216
|
+
*/
|
|
217
|
+
getTrackedDomains() {
|
|
218
|
+
return [...this.domains.keys()].sort();
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Get the total number of decisions across all domains.
|
|
222
|
+
*/
|
|
223
|
+
getTotalDecisions() {
|
|
224
|
+
let total = 0;
|
|
225
|
+
for (const state of this.domains.values()) {
|
|
226
|
+
total += state.decisionCount;
|
|
227
|
+
}
|
|
228
|
+
return total;
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Reset tracking data for a specific domain (or all domains).
|
|
232
|
+
*
|
|
233
|
+
* @param domain - Domain to reset, or undefined to reset all
|
|
234
|
+
*/
|
|
235
|
+
reset(domain) {
|
|
236
|
+
if (domain) {
|
|
237
|
+
this.domains.delete(domain);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
this.domains.clear();
|
|
241
|
+
this.alerts = [];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
// ==========================================================================
|
|
245
|
+
// Private Helpers
|
|
246
|
+
// ==========================================================================
|
|
247
|
+
/**
|
|
248
|
+
* Get or create domain tracking state.
|
|
249
|
+
*/
|
|
250
|
+
getOrCreateDomainState(domain) {
|
|
251
|
+
let state = this.domains.get(domain);
|
|
252
|
+
if (!state) {
|
|
253
|
+
state = {
|
|
254
|
+
points: [],
|
|
255
|
+
cumulativeRegret: 0,
|
|
256
|
+
decisionCount: 0,
|
|
257
|
+
lastGrowthRate: 'insufficient_data',
|
|
258
|
+
};
|
|
259
|
+
this.domains.set(domain, state);
|
|
260
|
+
}
|
|
261
|
+
return state;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Classify the growth rate of a regret curve using log-log regression.
|
|
265
|
+
*
|
|
266
|
+
* @param points - Regret data points
|
|
267
|
+
* @returns Growth rate classification
|
|
268
|
+
*/
|
|
269
|
+
classifyGrowthRate(points) {
|
|
270
|
+
if (points.length < MIN_DATA_POINTS_FOR_CLASSIFICATION) {
|
|
271
|
+
return 'insufficient_data';
|
|
272
|
+
}
|
|
273
|
+
const slope = this.computeLogLogSlope(points);
|
|
274
|
+
if (slope === undefined)
|
|
275
|
+
return 'insufficient_data';
|
|
276
|
+
if (slope < SUBLINEAR_THRESHOLD)
|
|
277
|
+
return 'sublinear';
|
|
278
|
+
if (slope <= SUPERLINEAR_THRESHOLD)
|
|
279
|
+
return 'linear';
|
|
280
|
+
return 'superlinear';
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Compute the slope of log(regret) vs log(n) for the regret curve.
|
|
284
|
+
*
|
|
285
|
+
* Filters out points where cumulative regret is zero (log(0) is undefined).
|
|
286
|
+
*
|
|
287
|
+
* @param points - Regret data points
|
|
288
|
+
* @returns Slope value, or undefined if insufficient valid data
|
|
289
|
+
*/
|
|
290
|
+
computeLogLogSlope(points) {
|
|
291
|
+
if (points.length < MIN_DATA_POINTS_FOR_CLASSIFICATION) {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
// Filter to points with positive regret and positive decision count
|
|
295
|
+
const validPoints = points.filter(p => p.cumulativeRegret > 0 && p.decisionCount > 0);
|
|
296
|
+
if (validPoints.length < 2)
|
|
297
|
+
return undefined;
|
|
298
|
+
const logN = validPoints.map(p => Math.log(p.decisionCount));
|
|
299
|
+
const logR = validPoints.map(p => Math.log(p.cumulativeRegret));
|
|
300
|
+
return linearRegressionSlope(logN, logR);
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Compute the average per-decision regret over the recent window.
|
|
304
|
+
*/
|
|
305
|
+
computeRecentAvgRegret(state) {
|
|
306
|
+
if (state.points.length < 2)
|
|
307
|
+
return 0;
|
|
308
|
+
const windowSize = Math.min(this.recentWindow, state.points.length);
|
|
309
|
+
const recentPoints = state.points.slice(-windowSize);
|
|
310
|
+
// Recent regret = difference in cumulative regret over the window
|
|
311
|
+
const firstPoint = recentPoints[0];
|
|
312
|
+
const lastPoint = recentPoints[recentPoints.length - 1];
|
|
313
|
+
const regretInWindow = lastPoint.cumulativeRegret - firstPoint.cumulativeRegret;
|
|
314
|
+
const decisionsInWindow = lastPoint.decisionCount - firstPoint.decisionCount;
|
|
315
|
+
if (decisionsInWindow <= 0)
|
|
316
|
+
return 0;
|
|
317
|
+
return regretInWindow / decisionsInWindow;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Emit a growth rate transition alert.
|
|
321
|
+
*/
|
|
322
|
+
emitAlert(domain, previousRate, newRate) {
|
|
323
|
+
const alert = {
|
|
324
|
+
domain,
|
|
325
|
+
previousRate,
|
|
326
|
+
newRate,
|
|
327
|
+
timestamp: Date.now(),
|
|
328
|
+
message: `Domain "${domain}" regret growth changed from ${previousRate} to ${newRate}`,
|
|
329
|
+
};
|
|
330
|
+
this.alerts.push(alert);
|
|
331
|
+
if (this.alerts.length > this.maxAlerts) {
|
|
332
|
+
this.alerts.shift();
|
|
333
|
+
}
|
|
334
|
+
logger.info('Regret growth rate transition', {
|
|
335
|
+
domain,
|
|
336
|
+
previousRate,
|
|
337
|
+
newRate,
|
|
338
|
+
});
|
|
339
|
+
for (const callback of this.alertCallbacks) {
|
|
340
|
+
try {
|
|
341
|
+
callback(alert);
|
|
342
|
+
}
|
|
343
|
+
catch (err) {
|
|
344
|
+
logger.error('Alert callback error', err instanceof Error ? err : undefined);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// ============================================================================
|
|
350
|
+
// Factory Function
|
|
351
|
+
// ============================================================================
|
|
352
|
+
/**
|
|
353
|
+
* Create a new RegretTracker instance.
|
|
354
|
+
*
|
|
355
|
+
* @param options - Optional configuration
|
|
356
|
+
* @returns New RegretTracker
|
|
357
|
+
*/
|
|
358
|
+
export function createRegretTracker(options) {
|
|
359
|
+
return new RegretTracker(options);
|
|
360
|
+
}
|
|
361
|
+
//# sourceMappingURL=regret-tracker.js.map
|