principles-disciple 1.79.0 → 1.81.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/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/index.ts +43 -0
- package/src/service/correction-observer-service.ts +200 -0
- package/src/service/evolution-worker.ts +2 -123
- package/tests/core/surface-guard.test.ts +198 -0
- package/tests/core/workspace-guidance-migrator.test.ts +201 -0
- package/tests/service/correction-observer-service.test.ts +331 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +41 -164
|
@@ -1,173 +1,50 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
const mockDb = {
|
|
14
|
-
listRecentSessions: vi.fn(() => [{ sessionId: 'session-1' }]),
|
|
15
|
-
listUserTurnsForSession: vi.fn(() => [{ rawExcerpt: 'User said wrong input', correctionDetected: true, correctionCue: 'wrong' }]),
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
const mockOptimizationService = {
|
|
19
|
-
buildTrajectoryHistory: vi.fn(async () => [
|
|
20
|
-
{ sessionId: 'session-1', timestamp: 'now', term: 'wrong', userMessage: '' }
|
|
21
|
-
]),
|
|
22
|
-
applyResult: vi.fn(),
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
// Mock dependencies
|
|
26
|
-
vi.mock('../../src/core/correction-cue-learner.js', () => ({
|
|
27
|
-
CorrectionCueLearner: { get: vi.fn(() => mockLearner) },
|
|
28
|
-
}));
|
|
29
|
-
|
|
30
|
-
vi.mock('../../src/core/trajectory.js', () => ({
|
|
31
|
-
TrajectoryRegistry: {
|
|
32
|
-
get: vi.fn(() => mockDb),
|
|
33
|
-
clear: vi.fn(),
|
|
34
|
-
},
|
|
35
|
-
}));
|
|
36
|
-
|
|
37
|
-
vi.mock('../../src/service/keyword-optimization-service.js', () => ({
|
|
38
|
-
KeywordOptimizationService: { get: vi.fn(() => mockOptimizationService) },
|
|
39
|
-
}));
|
|
40
|
-
|
|
41
|
-
// Mock principles-core runtime-v2 observer/scheduler classes
|
|
42
|
-
const mockDispatch = vi.fn().mockResolvedValue({
|
|
43
|
-
updated: true,
|
|
44
|
-
summary: 'Keyword store optimized',
|
|
45
|
-
updates: { wrong: { action: 'update', weight: 0.4, reasoning: 'slightly high FP' } }
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const mockRegister = vi.fn();
|
|
49
|
-
|
|
50
|
-
vi.mock('@principles/core/runtime-v2', () => {
|
|
51
|
-
return {
|
|
52
|
-
WorkflowFunnelLoader: class {
|
|
53
|
-
getFunnel = vi.fn(() => ({
|
|
54
|
-
policy: {
|
|
55
|
-
runtimeKind: 'pi-ai',
|
|
56
|
-
provider: 'anthropic',
|
|
57
|
-
model: 'anthropic/claude-3-5-sonnet',
|
|
58
|
-
apiKeyEnv: 'ANTHROPIC_API_KEY',
|
|
59
|
-
timeoutMs: 30000,
|
|
60
|
-
}
|
|
61
|
-
}));
|
|
62
|
-
},
|
|
63
|
-
PiAiRuntimeAdapter: class {},
|
|
64
|
-
CorrectionObserver: class {},
|
|
65
|
-
AgentScheduler: class {
|
|
66
|
-
register = mockRegister;
|
|
67
|
-
dispatch = mockDispatch;
|
|
68
|
-
}
|
|
69
|
-
};
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
// Import EvolutionWorkerService
|
|
73
|
-
import { EvolutionWorkerService } from '../../src/service/evolution-worker.js';
|
|
74
|
-
import { safeRmDir } from '../test-utils.js';
|
|
75
|
-
|
|
76
|
-
function createMockApi() {
|
|
77
|
-
return {
|
|
78
|
-
logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), debug: vi.fn() },
|
|
79
|
-
runtime: {
|
|
80
|
-
agent: { runEmbeddedPiAgent: vi.fn() },
|
|
81
|
-
system: {
|
|
82
|
-
requestHeartbeatNow: vi.fn(),
|
|
83
|
-
runHeartbeatOnce: vi.fn(),
|
|
84
|
-
},
|
|
85
|
-
},
|
|
86
|
-
} as any;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const fastPollConfig = { get: (k: string) => k === 'intervals.worker_poll_ms' ? 1000 : undefined };
|
|
90
|
-
|
|
91
|
-
describe('EvolutionWorkerService Correction Observer Integration', () => {
|
|
92
|
-
beforeEach(() => {
|
|
93
|
-
vi.useFakeTimers();
|
|
94
|
-
vi.clearAllMocks();
|
|
95
|
-
EvolutionWorkerService.api = null;
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
afterEach(() => {
|
|
99
|
-
vi.useRealTimers();
|
|
100
|
-
EvolutionWorkerService.api = null;
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { PLUGIN_SURFACE_REGISTRY } from '@principles/core/runtime-v2';
|
|
3
|
+
import { DEFAULT_FEATURE_FLAGS, computeEffectiveFlags } from '@principles/core/runtime-v2';
|
|
4
|
+
|
|
5
|
+
describe('Correction Observer Ownership — Feature Flag & Surface Registry Consistency (PRI-293, ERR-027)', () => {
|
|
6
|
+
it('correction_observer feature flag is registered as quiet with enabled:true (disableable runtime kill switch)', () => {
|
|
7
|
+
const flag = DEFAULT_FEATURE_FLAGS.find(f => f.id === 'correction_observer');
|
|
8
|
+
expect(flag).toBeDefined();
|
|
9
|
+
expect(flag!.category).toBe('quiet');
|
|
10
|
+
expect(flag!.enabled).toBe(true);
|
|
101
11
|
});
|
|
102
12
|
|
|
103
|
-
it('
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
// Initialize an empty queue to avoid processing actual queue items
|
|
109
|
-
fs.writeFileSync(
|
|
110
|
-
path.join(stateDir, 'evolution_queue.json'),
|
|
111
|
-
JSON.stringify([], null, 2),
|
|
112
|
-
'utf8'
|
|
13
|
+
it('correction_observer can be disabled via workspace config (P1 fix — runtime kill switch)', () => {
|
|
14
|
+
const result = computeEffectiveFlags(
|
|
15
|
+
{ correction_observer: { enabled: false } },
|
|
16
|
+
DEFAULT_FEATURE_FLAGS,
|
|
17
|
+
'.pd/feature-flags.yaml',
|
|
113
18
|
);
|
|
19
|
+
expect(result.flags['correction_observer'].enabled).toBe(false);
|
|
20
|
+
expect(result.warnings).not.toContain(expect.stringContaining('core flag cannot be disabled'));
|
|
21
|
+
});
|
|
114
22
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
}, null, 2),
|
|
123
|
-
'utf8'
|
|
124
|
-
);
|
|
125
|
-
|
|
126
|
-
// Invalidate workspace cache to load the newly written settings
|
|
127
|
-
WorkspaceContext.clearCache();
|
|
128
|
-
ConfigService.reset();
|
|
129
|
-
|
|
130
|
-
const mockApi = createMockApi();
|
|
131
|
-
EvolutionWorkerService.api = mockApi;
|
|
132
|
-
|
|
133
|
-
try {
|
|
134
|
-
EvolutionWorkerService.start({
|
|
135
|
-
workspaceDir,
|
|
136
|
-
stateDir,
|
|
137
|
-
logger: mockApi.logger,
|
|
138
|
-
config: fastPollConfig,
|
|
139
|
-
api: mockApi,
|
|
140
|
-
} as any);
|
|
141
|
-
|
|
142
|
-
// 1. Advance to startup timer (5000ms) and wait for microtasks to settle
|
|
143
|
-
await vi.advanceTimersByTimeAsync(5000);
|
|
144
|
-
for (let i = 0; i < 20; i++) {
|
|
145
|
-
await Promise.resolve();
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// 2. Advance past the poll interval (1000ms) to trigger runCycle
|
|
149
|
-
await vi.advanceTimersByTimeAsync(1050);
|
|
150
|
-
for (let i = 0; i < 20; i++) {
|
|
151
|
-
await Promise.resolve();
|
|
152
|
-
}
|
|
23
|
+
it('service:correction-observer surface is registered as core with enabledByDefault:true', () => {
|
|
24
|
+
const surface = PLUGIN_SURFACE_REGISTRY.find(s => s.id === 'service:correction-observer');
|
|
25
|
+
expect(surface).toBeDefined();
|
|
26
|
+
expect(surface!.category).toBe('core');
|
|
27
|
+
expect(surface!.enabledByDefault).toBe(true);
|
|
28
|
+
});
|
|
153
29
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}));
|
|
30
|
+
it('startup:correction-observer surface is registered as core with enabledByDefault:true', () => {
|
|
31
|
+
const surface = PLUGIN_SURFACE_REGISTRY.find(s => s.id === 'startup:correction-observer');
|
|
32
|
+
expect(surface).toBeDefined();
|
|
33
|
+
expect(surface!.category).toBe('core');
|
|
34
|
+
expect(surface!.enabledByDefault).toBe(true);
|
|
35
|
+
});
|
|
161
36
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
37
|
+
it('evolution_worker feature flag remains quiet with enabled:false', () => {
|
|
38
|
+
const flag = DEFAULT_FEATURE_FLAGS.find(f => f.id === 'evolution_worker');
|
|
39
|
+
expect(flag).toBeDefined();
|
|
40
|
+
expect(flag!.category).toBe('quiet');
|
|
41
|
+
expect(flag!.enabled).toBe(false);
|
|
42
|
+
});
|
|
167
43
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
44
|
+
it('service:evolution-worker surface remains quiet with enabledByDefault:false', () => {
|
|
45
|
+
const surface = PLUGIN_SURFACE_REGISTRY.find(s => s.id === 'service:evolution-worker');
|
|
46
|
+
expect(surface).toBeDefined();
|
|
47
|
+
expect(surface!.category).toBe('quiet');
|
|
48
|
+
expect(surface!.enabledByDefault).toBe(false);
|
|
172
49
|
});
|
|
173
50
|
});
|