claude-mycelium 2.0.0 → 2.2.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/.agent-meta/_inhibitors.ndjson +1287 -0
- package/.agent-meta/_quarantine.json +45 -0
- package/.agent-meta/config.json +9 -0
- package/.agent-meta/tasks/_active.json +4 -0
- package/.agent-meta/tasks/task_0657b028-05a0-4b0c-b0b9-a4eae3d66cd9.json +168 -0
- package/.claude/memory.db +0 -0
- package/.claude/settings.local.json +4 -1
- package/README.md +85 -233
- package/SECURITY.md +145 -0
- package/dist/agent/task-worker.d.ts +11 -0
- package/dist/agent/task-worker.d.ts.map +1 -0
- package/dist/agent/task-worker.js +173 -0
- package/dist/agent/task-worker.js.map +1 -0
- package/dist/agent/worker.d.ts +8 -0
- package/dist/agent/worker.d.ts.map +1 -0
- package/dist/agent/worker.js +97 -0
- package/dist/agent/worker.js.map +1 -0
- package/dist/bin.d.ts +7 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +11 -0
- package/dist/bin.js.map +1 -0
- package/dist/cli/cost.d.ts +10 -0
- package/dist/cli/cost.d.ts.map +1 -0
- package/dist/cli/cost.js +163 -0
- package/dist/cli/cost.js.map +1 -0
- package/dist/cli/gc.d.ts +10 -0
- package/dist/cli/gc.d.ts.map +1 -0
- package/dist/cli/gc.js +108 -0
- package/dist/cli/gc.js.map +1 -0
- package/dist/cli/gradients.d.ts +10 -0
- package/dist/cli/gradients.d.ts.map +1 -0
- package/dist/cli/gradients.js +70 -0
- package/dist/cli/gradients.js.map +1 -0
- package/dist/cli/grow.d.ts +17 -0
- package/dist/cli/grow.d.ts.map +1 -0
- package/dist/cli/grow.js +373 -0
- package/dist/cli/grow.js.map +1 -0
- package/dist/cli/index.d.ts +17 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +74 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +11 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +97 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/status.d.ts +10 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +191 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/coordination/file-locks.d.ts +42 -0
- package/dist/coordination/file-locks.d.ts.map +1 -0
- package/dist/coordination/file-locks.js +269 -0
- package/dist/coordination/file-locks.js.map +1 -0
- package/dist/coordination/index.d.ts +4 -0
- package/dist/coordination/index.d.ts.map +1 -1
- package/dist/coordination/index.js +4 -0
- package/dist/coordination/index.js.map +1 -1
- package/dist/coordination/inhibitors.d.ts +84 -0
- package/dist/coordination/inhibitors.d.ts.map +1 -0
- package/dist/coordination/inhibitors.js +290 -0
- package/dist/coordination/inhibitors.js.map +1 -0
- package/dist/coordination/process-manager.d.ts +73 -0
- package/dist/coordination/process-manager.d.ts.map +1 -0
- package/dist/coordination/process-manager.js +144 -0
- package/dist/coordination/process-manager.js.map +1 -0
- package/dist/core/agent-executor.d.ts +4 -1
- package/dist/core/agent-executor.d.ts.map +1 -1
- package/dist/core/agent-executor.js +38 -12
- package/dist/core/agent-executor.js.map +1 -1
- package/dist/core/change-applier.d.ts +29 -5
- package/dist/core/change-applier.d.ts.map +1 -1
- package/dist/core/change-applier.js +254 -24
- package/dist/core/change-applier.js.map +1 -1
- package/dist/core/signals/churn.d.ts.map +1 -1
- package/dist/core/signals/churn.js +6 -4
- package/dist/core/signals/churn.js.map +1 -1
- package/dist/core/signals/debt.d.ts.map +1 -1
- package/dist/core/signals/debt.js +4 -3
- package/dist/core/signals/debt.js.map +1 -1
- package/dist/cost/cost-tracker.d.ts.map +1 -1
- package/dist/cost/cost-tracker.js +2 -0
- package/dist/cost/cost-tracker.js.map +1 -1
- package/dist/gc/index.d.ts +17 -0
- package/dist/gc/index.d.ts.map +1 -0
- package/dist/gc/index.js +17 -0
- package/dist/gc/index.js.map +1 -0
- package/dist/gc/runner.d.ts +39 -0
- package/dist/gc/runner.d.ts.map +1 -0
- package/dist/gc/runner.js +277 -0
- package/dist/gc/runner.js.map +1 -0
- package/dist/gc/trace-compactor.d.ts +31 -0
- package/dist/gc/trace-compactor.d.ts.map +1 -0
- package/dist/gc/trace-compactor.js +162 -0
- package/dist/gc/trace-compactor.js.map +1 -0
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -1
- package/dist/index.js.map +1 -1
- package/dist/prompts/index.d.ts +2 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/index.js.map +1 -1
- package/dist/quarantine/explorer.d.ts +65 -0
- package/dist/quarantine/explorer.d.ts.map +1 -0
- package/dist/quarantine/explorer.js +175 -0
- package/dist/quarantine/explorer.js.map +1 -0
- package/dist/quarantine/index.d.ts +7 -0
- package/dist/quarantine/index.d.ts.map +1 -0
- package/dist/quarantine/index.js +7 -0
- package/dist/quarantine/index.js.map +1 -0
- package/dist/quarantine/manager.d.ts +75 -0
- package/dist/quarantine/manager.d.ts.map +1 -0
- package/dist/quarantine/manager.js +275 -0
- package/dist/quarantine/manager.js.map +1 -0
- package/dist/task/acceptance.d.ts +29 -0
- package/dist/task/acceptance.d.ts.map +1 -0
- package/dist/task/acceptance.js +228 -0
- package/dist/task/acceptance.js.map +1 -0
- package/dist/task/agent-coordinator.d.ts +40 -0
- package/dist/task/agent-coordinator.d.ts.map +1 -0
- package/dist/task/agent-coordinator.js +168 -0
- package/dist/task/agent-coordinator.js.map +1 -0
- package/dist/task/executor.d.ts +37 -0
- package/dist/task/executor.d.ts.map +1 -0
- package/dist/task/executor.js +462 -0
- package/dist/task/executor.js.map +1 -0
- package/dist/task/index.d.ts +12 -0
- package/dist/task/index.d.ts.map +1 -0
- package/dist/task/index.js +12 -0
- package/dist/task/index.js.map +1 -0
- package/dist/task/planner.d.ts +21 -0
- package/dist/task/planner.d.ts.map +1 -0
- package/dist/task/planner.js +253 -0
- package/dist/task/planner.js.map +1 -0
- package/dist/task/storage.d.ts +46 -0
- package/dist/task/storage.d.ts.map +1 -0
- package/dist/task/storage.js +266 -0
- package/dist/task/storage.js.map +1 -0
- package/dist/trace/trace-event.d.ts +2 -18
- package/dist/trace/trace-event.d.ts.map +1 -1
- package/dist/trace/trace-event.js +6 -6
- package/dist/trace/trace-event.js.map +1 -1
- package/dist/utils/file-utils.d.ts.map +1 -1
- package/dist/utils/file-utils.js +54 -15
- package/dist/utils/file-utils.js.map +1 -1
- package/docs/PHASE5_IMPLEMENTATION.md +237 -0
- package/docs/PHASES-3-7-COMPLETE.md +177 -0
- package/docs/PHASE_4_COMPLETE.md +135 -0
- package/docs/PHASE_7_DELIVERABLES.md +295 -0
- package/docs/PHASE_7_IMPLEMENTATION.md +306 -0
- package/docs/PHASE_7_SUMMARY.txt +195 -0
- package/docs/RELEASE-NOTES-v2.1.md +213 -0
- package/docs/ROADMAP.md +194 -107
- package/docs/SECURITY-AUDIT.md +387 -0
- package/docs/SNAPSHOT.md +59 -32
- package/docs/implementation/phase3-summary.md +220 -0
- package/package.json +27 -11
- package/src/agent/task-worker.ts +196 -0
- package/src/agent/worker.ts +111 -0
- package/src/bin.ts +13 -0
- package/src/cli/cost.ts +210 -0
- package/src/cli/gc.ts +138 -0
- package/src/cli/gradients.ts +97 -0
- package/src/cli/grow.ts +416 -0
- package/src/cli/index.ts +81 -0
- package/src/cli/init.ts +139 -0
- package/src/cli/status.ts +218 -0
- package/src/coordination/file-locks.ts +300 -0
- package/src/coordination/index.ts +4 -0
- package/src/coordination/inhibitors.ts +345 -0
- package/src/coordination/process-manager.ts +199 -0
- package/src/core/agent-executor.ts +37 -8
- package/src/core/signals/churn.ts +8 -5
- package/src/core/signals/debt.ts +4 -3
- package/src/cost/cost-tracker.ts +2 -0
- package/src/gc/index.ts +17 -0
- package/src/gc/runner.ts +314 -0
- package/src/gc/trace-compactor.ts +187 -0
- package/src/index.ts +7 -1
- package/src/prompts/index.ts +2 -1
- package/src/quarantine/explorer.ts +234 -0
- package/src/quarantine/index.ts +7 -0
- package/src/quarantine/manager.ts +336 -0
- package/src/task/acceptance.ts +267 -0
- package/src/task/agent-coordinator.ts +220 -0
- package/src/task/executor.ts +543 -0
- package/src/task/index.ts +38 -0
- package/src/task/planner.ts +294 -0
- package/src/task/storage.ts +332 -0
- package/src/trace/trace-event.ts +7 -26
- package/src/utils/file-utils.ts +61 -15
- package/tests/cli/gc.test.ts +206 -0
- package/tests/cli/init.test.ts +181 -0
- package/tests/cli/status.test.ts +282 -0
- package/tests/coordination/file-locks.test.ts +196 -0
- package/tests/coordination/inhibitors.test.ts +459 -0
- package/tests/coordination/integration.test.ts +195 -0
- package/tests/coordination/process-manager.test.ts +165 -0
- package/tests/gc/trace-compactor.test.ts +245 -0
- package/tests/integration/phase-7.test.ts +145 -0
- package/tests/quarantine/explorer.test.ts +381 -0
- package/tests/quarantine/manager.test.ts +399 -0
- package/tests/security/command-injection.test.ts +88 -0
- package/tests/security/path-traversal.test.ts +103 -0
- package/tests/task/acceptance.test.ts +411 -0
- package/tests/task/executor.test.ts +421 -0
- package/tests/task/planner.test.ts +359 -0
- package/tests/trace/trace-event.test.ts +62 -20
- package/tsconfig.json +2 -2
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Explorer Mode Spawning
|
|
3
|
+
* Validates probability calculations and target selection
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import {
|
|
9
|
+
shouldSpawnExplorer,
|
|
10
|
+
selectExplorerTarget,
|
|
11
|
+
calculateExplorerProbability,
|
|
12
|
+
shouldSpawnExplorerForFile,
|
|
13
|
+
getExplorerConfig,
|
|
14
|
+
formatExplorerInstructions,
|
|
15
|
+
} from '../../src/quarantine/explorer.js';
|
|
16
|
+
import { addToQuarantine } from '../../src/quarantine/manager.js';
|
|
17
|
+
import type { Gradient } from '../../src/types/index.js';
|
|
18
|
+
import type { TraceEvent } from '../../src/trace/trace-event.js';
|
|
19
|
+
|
|
20
|
+
describe('Explorer Mode', () => {
|
|
21
|
+
let TEST_DIR: string;
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
// Create unique test directory for each test
|
|
25
|
+
TEST_DIR = `.agent-meta-test-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
26
|
+
|
|
27
|
+
if (!fs.existsSync(TEST_DIR)) {
|
|
28
|
+
fs.mkdirSync(TEST_DIR, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
process.env.TEST_META_DIR = TEST_DIR;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
// Cleanup test files
|
|
36
|
+
if (fs.existsSync(TEST_DIR)) {
|
|
37
|
+
fs.rmSync(TEST_DIR, { recursive: true, force: true });
|
|
38
|
+
}
|
|
39
|
+
// Clean up environment
|
|
40
|
+
delete process.env.TEST_META_DIR;
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const createMockTrace = (efficiency: number): TraceEvent => ({
|
|
44
|
+
id: `trace-${Date.now()}-${Math.random()}`,
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
agent_id: 'test-agent',
|
|
47
|
+
file_path: 'test.ts',
|
|
48
|
+
mode: 'error_reducer',
|
|
49
|
+
gradient_before: 0.5,
|
|
50
|
+
gradient_after: 0.48,
|
|
51
|
+
gradient_delta: 0.02,
|
|
52
|
+
changes_made: [],
|
|
53
|
+
tokens_used: 1000,
|
|
54
|
+
cost_usd: 0.01,
|
|
55
|
+
duration_ms: 1000,
|
|
56
|
+
success: true,
|
|
57
|
+
efficiency,
|
|
58
|
+
ci_passed: true,
|
|
59
|
+
changes: {
|
|
60
|
+
additions: 1,
|
|
61
|
+
deletions: 1,
|
|
62
|
+
files_touched: ['test.ts'],
|
|
63
|
+
},
|
|
64
|
+
cost: {
|
|
65
|
+
tokens_in: 500,
|
|
66
|
+
tokens_out: 500,
|
|
67
|
+
model: 'claude-sonnet-4',
|
|
68
|
+
estimated_usd: 0.01,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe('calculateExplorerProbability', () => {
|
|
73
|
+
it('should calculate 10% probability for centrality 0.0', () => {
|
|
74
|
+
const prob = calculateExplorerProbability(0.0);
|
|
75
|
+
expect(prob).toBeCloseTo(0.1, 3);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('should calculate 15% probability for centrality 0.5', () => {
|
|
79
|
+
const prob = calculateExplorerProbability(0.5);
|
|
80
|
+
expect(prob).toBeCloseTo(0.15, 3);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('should calculate 20% probability for centrality 1.0', () => {
|
|
84
|
+
const prob = calculateExplorerProbability(1.0);
|
|
85
|
+
expect(prob).toBeCloseTo(0.2, 3);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should follow formula: 0.1 × (1 + centrality)', () => {
|
|
89
|
+
const centrality = 0.75;
|
|
90
|
+
const expected = 0.1 * (1 + centrality);
|
|
91
|
+
const actual = calculateExplorerProbability(centrality);
|
|
92
|
+
expect(actual).toBeCloseTo(expected, 5);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('shouldSpawnExplorer', () => {
|
|
97
|
+
it('should return false with insufficient data (<10 traces)', async () => {
|
|
98
|
+
const getRecentTraces = async (limit: number) => {
|
|
99
|
+
return Array(5)
|
|
100
|
+
.fill(0)
|
|
101
|
+
.map(() => createMockTrace(0.1));
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const result = await shouldSpawnExplorer(getRecentTraces);
|
|
105
|
+
expect(result).toBe(false);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('should have low probability with high efficiency (>=0.3)', async () => {
|
|
109
|
+
const getRecentTraces = async (limit: number) => {
|
|
110
|
+
return Array(20)
|
|
111
|
+
.fill(0)
|
|
112
|
+
.map(() => createMockTrace(0.35)); // High efficiency
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// Run multiple times to check probability
|
|
116
|
+
let spawnCount = 0;
|
|
117
|
+
const iterations = 100;
|
|
118
|
+
|
|
119
|
+
for (let i = 0; i < iterations; i++) {
|
|
120
|
+
if (await shouldSpawnExplorer(getRecentTraces)) {
|
|
121
|
+
spawnCount++;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const probability = spawnCount / iterations;
|
|
126
|
+
// Should be around 5% (base probability)
|
|
127
|
+
expect(probability).toBeLessThan(0.15); // Allow some variance
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should have medium probability with medium efficiency (0.15)', async () => {
|
|
131
|
+
const getRecentTraces = async (limit: number) => {
|
|
132
|
+
return Array(20)
|
|
133
|
+
.fill(0)
|
|
134
|
+
.map(() => createMockTrace(0.15)); // Medium efficiency
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
// Run multiple times to check probability
|
|
138
|
+
let spawnCount = 0;
|
|
139
|
+
const iterations = 200;
|
|
140
|
+
|
|
141
|
+
for (let i = 0; i < iterations; i++) {
|
|
142
|
+
if (await shouldSpawnExplorer(getRecentTraces)) {
|
|
143
|
+
spawnCount++;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const probability = spawnCount / iterations;
|
|
148
|
+
// Should be around 15% (halfway to max)
|
|
149
|
+
expect(probability).toBeGreaterThan(0.08); // Lower bound
|
|
150
|
+
expect(probability).toBeLessThan(0.22); // Upper bound
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should have high probability with low efficiency (0.0)', async () => {
|
|
154
|
+
const getRecentTraces = async (limit: number) => {
|
|
155
|
+
return Array(20)
|
|
156
|
+
.fill(0)
|
|
157
|
+
.map(() => createMockTrace(0.0)); // Zero efficiency
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// Run multiple times to check probability
|
|
161
|
+
let spawnCount = 0;
|
|
162
|
+
const iterations = 200;
|
|
163
|
+
|
|
164
|
+
for (let i = 0; i < iterations; i++) {
|
|
165
|
+
if (await shouldSpawnExplorer(getRecentTraces)) {
|
|
166
|
+
spawnCount++;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const probability = spawnCount / iterations;
|
|
171
|
+
// Should be around 25% (max probability)
|
|
172
|
+
expect(probability).toBeGreaterThan(0.18); // Lower bound
|
|
173
|
+
expect(probability).toBeLessThan(0.32); // Upper bound
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('selectExplorerTarget', () => {
|
|
178
|
+
const mockIsFileLocked = async (file: string) => false;
|
|
179
|
+
|
|
180
|
+
it('should prioritize quarantined files with attempts remaining', async () => {
|
|
181
|
+
// Add quarantined file
|
|
182
|
+
await addToQuarantine({
|
|
183
|
+
file: 'quarantined.ts',
|
|
184
|
+
quarantined_at: new Date().toISOString(),
|
|
185
|
+
reason: 'Test',
|
|
186
|
+
attempts_before_quarantine: 10,
|
|
187
|
+
explorer_attempts: 0,
|
|
188
|
+
max_explorer_attempts: 3,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
const gradients: Gradient[] = [
|
|
192
|
+
{
|
|
193
|
+
file: 'high-gradient.ts',
|
|
194
|
+
score: 0.9,
|
|
195
|
+
baseScore: 0.9,
|
|
196
|
+
impactMultiplier: 1.0,
|
|
197
|
+
efficiencyPenalty: 1.0,
|
|
198
|
+
dominantSignal: 'complexity',
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
file: 'quarantined.ts',
|
|
202
|
+
score: 0.5,
|
|
203
|
+
baseScore: 0.5,
|
|
204
|
+
impactMultiplier: 1.0,
|
|
205
|
+
efficiencyPenalty: 1.0,
|
|
206
|
+
dominantSignal: 'error_rate',
|
|
207
|
+
},
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
const target = await selectExplorerTarget(gradients, mockIsFileLocked);
|
|
211
|
+
|
|
212
|
+
// Should pick quarantined file even though high-gradient.ts has higher score
|
|
213
|
+
expect(target).toBe('quarantined.ts');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('should skip quarantined files with exhausted attempts', async () => {
|
|
217
|
+
// Add quarantined file with exhausted attempts
|
|
218
|
+
await addToQuarantine({
|
|
219
|
+
file: 'exhausted.ts',
|
|
220
|
+
quarantined_at: new Date().toISOString(),
|
|
221
|
+
reason: 'Test',
|
|
222
|
+
attempts_before_quarantine: 10,
|
|
223
|
+
explorer_attempts: 3, // Exhausted
|
|
224
|
+
max_explorer_attempts: 3,
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
const gradients: Gradient[] = [
|
|
228
|
+
{
|
|
229
|
+
file: 'high-gradient.ts',
|
|
230
|
+
score: 0.9,
|
|
231
|
+
baseScore: 0.9,
|
|
232
|
+
impactMultiplier: 1.0,
|
|
233
|
+
efficiencyPenalty: 1.0,
|
|
234
|
+
dominantSignal: 'complexity',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
file: 'exhausted.ts',
|
|
238
|
+
score: 0.8,
|
|
239
|
+
baseScore: 0.8,
|
|
240
|
+
impactMultiplier: 1.0,
|
|
241
|
+
efficiencyPenalty: 1.0,
|
|
242
|
+
dominantSignal: 'error_rate',
|
|
243
|
+
},
|
|
244
|
+
];
|
|
245
|
+
|
|
246
|
+
const target = await selectExplorerTarget(gradients, mockIsFileLocked);
|
|
247
|
+
|
|
248
|
+
// Should pick high-gradient file since quarantined one is exhausted
|
|
249
|
+
expect(target).toBe('high-gradient.ts');
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it('should pick highest gradient among non-quarantined files', async () => {
|
|
253
|
+
const gradients: Gradient[] = [
|
|
254
|
+
{
|
|
255
|
+
file: 'low.ts',
|
|
256
|
+
score: 0.3,
|
|
257
|
+
baseScore: 0.3,
|
|
258
|
+
impactMultiplier: 1.0,
|
|
259
|
+
efficiencyPenalty: 1.0,
|
|
260
|
+
dominantSignal: 'churn',
|
|
261
|
+
},
|
|
262
|
+
{
|
|
263
|
+
file: 'high.ts',
|
|
264
|
+
score: 0.9,
|
|
265
|
+
baseScore: 0.9,
|
|
266
|
+
impactMultiplier: 1.0,
|
|
267
|
+
efficiencyPenalty: 1.0,
|
|
268
|
+
dominantSignal: 'complexity',
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
file: 'medium.ts',
|
|
272
|
+
score: 0.6,
|
|
273
|
+
baseScore: 0.6,
|
|
274
|
+
impactMultiplier: 1.0,
|
|
275
|
+
efficiencyPenalty: 1.0,
|
|
276
|
+
dominantSignal: 'debt',
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
|
|
280
|
+
const target = await selectExplorerTarget(gradients, mockIsFileLocked);
|
|
281
|
+
expect(target).toBe('high.ts');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should skip locked files', async () => {
|
|
285
|
+
const mockIsFileLockedSelective = async (file: string) => file === 'locked.ts';
|
|
286
|
+
|
|
287
|
+
const gradients: Gradient[] = [
|
|
288
|
+
{
|
|
289
|
+
file: 'locked.ts',
|
|
290
|
+
score: 0.9,
|
|
291
|
+
baseScore: 0.9,
|
|
292
|
+
impactMultiplier: 1.0,
|
|
293
|
+
efficiencyPenalty: 1.0,
|
|
294
|
+
dominantSignal: 'complexity',
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
file: 'unlocked.ts',
|
|
298
|
+
score: 0.7,
|
|
299
|
+
baseScore: 0.7,
|
|
300
|
+
impactMultiplier: 1.0,
|
|
301
|
+
efficiencyPenalty: 1.0,
|
|
302
|
+
dominantSignal: 'error_rate',
|
|
303
|
+
},
|
|
304
|
+
];
|
|
305
|
+
|
|
306
|
+
const target = await selectExplorerTarget(gradients, mockIsFileLockedSelective);
|
|
307
|
+
expect(target).toBe('unlocked.ts');
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should return null if no valid targets', async () => {
|
|
311
|
+
const mockAllLocked = async (file: string) => true;
|
|
312
|
+
|
|
313
|
+
const gradients: Gradient[] = [
|
|
314
|
+
{
|
|
315
|
+
file: 'locked1.ts',
|
|
316
|
+
score: 0.9,
|
|
317
|
+
baseScore: 0.9,
|
|
318
|
+
impactMultiplier: 1.0,
|
|
319
|
+
efficiencyPenalty: 1.0,
|
|
320
|
+
dominantSignal: 'complexity',
|
|
321
|
+
},
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
const target = await selectExplorerTarget(gradients, mockAllLocked);
|
|
325
|
+
expect(target).toBeNull();
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
describe('shouldSpawnExplorerForFile', () => {
|
|
330
|
+
it('should be probabilistic based on centrality', () => {
|
|
331
|
+
// Run many times to verify probability distribution
|
|
332
|
+
const centrality = 0.5; // Should give ~15% probability
|
|
333
|
+
let spawnCount = 0;
|
|
334
|
+
const iterations = 1000;
|
|
335
|
+
|
|
336
|
+
for (let i = 0; i < iterations; i++) {
|
|
337
|
+
if (shouldSpawnExplorerForFile(centrality)) {
|
|
338
|
+
spawnCount++;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const probability = spawnCount / iterations;
|
|
343
|
+
expect(probability).toBeGreaterThan(0.10); // Lower bound
|
|
344
|
+
expect(probability).toBeLessThan(0.20); // Upper bound
|
|
345
|
+
});
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
describe('getExplorerConfig', () => {
|
|
349
|
+
it('should return explorer configuration', () => {
|
|
350
|
+
const config = getExplorerConfig();
|
|
351
|
+
|
|
352
|
+
expect(config.temperature).toBeGreaterThan(0);
|
|
353
|
+
expect(config.systemPromptAddition).toContain('EXPLORER');
|
|
354
|
+
expect(config.systemPromptAddition).toContain('creative');
|
|
355
|
+
});
|
|
356
|
+
|
|
357
|
+
it('should have higher temperature than normal modes', () => {
|
|
358
|
+
const config = getExplorerConfig();
|
|
359
|
+
// Normal modes typically use 0.3-0.5, explorer should be higher
|
|
360
|
+
expect(config.temperature).toBeGreaterThanOrEqual(0.7);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
describe('formatExplorerInstructions', () => {
|
|
365
|
+
it('should format instructions for quarantined file', () => {
|
|
366
|
+
const instructions = formatExplorerInstructions('test.ts', true, 2);
|
|
367
|
+
|
|
368
|
+
expect(instructions).toContain('QUARANTINED');
|
|
369
|
+
expect(instructions).toContain('test.ts');
|
|
370
|
+
expect(instructions).toContain('multiple');
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
it('should format instructions for non-quarantined file', () => {
|
|
374
|
+
const instructions = formatExplorerInstructions('test.ts', false, 0);
|
|
375
|
+
|
|
376
|
+
expect(instructions).toContain('Creative Exploration');
|
|
377
|
+
expect(instructions).toContain('test.ts');
|
|
378
|
+
expect(instructions).not.toContain('QUARANTINED');
|
|
379
|
+
});
|
|
380
|
+
});
|
|
381
|
+
});
|