@zoebuildsai/trace 1.5.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/.gitignore +115 -0
- package/.trace/progress.json +22 -0
- package/README.md +466 -0
- package/RELEASE-NOTES-1.5.0.md +410 -0
- package/STATUS.md +245 -0
- package/dist/auto-commit.d.ts +66 -0
- package/dist/auto-commit.d.ts.map +1 -0
- package/dist/auto-commit.js +180 -0
- package/dist/auto-commit.js.map +1 -0
- package/dist/cli.d.ts +7 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +246 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands.d.ts +46 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +256 -0
- package/dist/commands.js.map +1 -0
- package/dist/diff.d.ts +23 -0
- package/dist/diff.d.ts.map +1 -0
- package/dist/diff.js +106 -0
- package/dist/diff.js.map +1 -0
- package/dist/github.d.ts.map +1 -0
- package/dist/github.js.map +1 -0
- package/dist/index-cache.d.ts +35 -0
- package/dist/index-cache.d.ts.map +1 -0
- package/dist/index-cache.js +114 -0
- package/dist/index-cache.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/storage.d.ts +45 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +151 -0
- package/dist/storage.js.map +1 -0
- package/dist/sync.d.ts +60 -0
- package/dist/sync.js +184 -0
- package/dist/tags.d.ts +85 -0
- package/dist/tags.d.ts.map +1 -0
- package/dist/tags.js +219 -0
- package/dist/tags.js.map +1 -0
- package/dist/types.d.ts +102 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/dist/types.js.map +1 -0
- package/docs/.nojekyll +0 -0
- package/docs/README.md +73 -0
- package/docs/_config.yml +2 -0
- package/docs/index.html +960 -0
- package/docs-website/package.json +20 -0
- package/jest.config.js +21 -0
- package/package.json +50 -0
- package/scripts/init.ts +290 -0
- package/src/agent-audit.ts +270 -0
- package/src/agent-checkout.ts +227 -0
- package/src/agent-coordination.ts +318 -0
- package/src/async-queue.ts +203 -0
- package/src/auto-branching.ts +279 -0
- package/src/auto-commit.ts +166 -0
- package/src/cherry-pick.ts +252 -0
- package/src/chunked-upload.ts +224 -0
- package/src/cli-v2.ts +335 -0
- package/src/cli.ts +318 -0
- package/src/cliff-detection.ts +232 -0
- package/src/commands.ts +267 -0
- package/src/commit-hash-system.ts +351 -0
- package/src/compression.ts +176 -0
- package/src/conflict-resolution-ui.ts +277 -0
- package/src/conflict-visualization.ts +238 -0
- package/src/diff-formatter.ts +184 -0
- package/src/diff.ts +124 -0
- package/src/distributed-coordination.ts +273 -0
- package/src/git-interop.ts +316 -0
- package/src/index-cache.ts +88 -0
- package/src/index.ts +38 -0
- package/src/merge-engine.ts +143 -0
- package/src/message-search.ts +370 -0
- package/src/performance-monitoring.ts +236 -0
- package/src/rebase.ts +327 -0
- package/src/rollback.ts +215 -0
- package/src/semantic-grouping.ts +245 -0
- package/src/stage-area.ts +324 -0
- package/src/stash.ts +278 -0
- package/src/storage.ts +131 -0
- package/src/sync.ts +205 -0
- package/src/tags.ts +244 -0
- package/src/types.ts +119 -0
- package/src/webhooks.ts +119 -0
- package/src/workspace-isolation.ts +298 -0
- package/tests/auto-commit.test.ts +308 -0
- package/tests/checkout.test.ts +136 -0
- package/tests/commit.test.ts +118 -0
- package/tests/diff.test.ts +191 -0
- package/tests/github.test.ts +94 -0
- package/tests/integration.test.ts +267 -0
- package/tests/log.test.ts +125 -0
- package/tests/phase2-integration.test.ts +370 -0
- package/tests/storage.test.ts +167 -0
- package/tests/tags.test.ts +477 -0
- package/tests/types.test.ts +75 -0
- package/tests/v1.1/agent-audit.test.ts +472 -0
- package/tests/v1.1/agent-coordination.test.ts +308 -0
- package/tests/v1.1/async-queue.test.ts +253 -0
- package/tests/v1.1/comprehensive.test.ts +521 -0
- package/tests/v1.1/diff-formatter.test.ts +238 -0
- package/tests/v1.1/integration.test.ts +389 -0
- package/tests/v1.1/onboarding.test.ts +365 -0
- package/tests/v1.1/rollback.test.ts +370 -0
- package/tests/v1.1/semantic-grouping.test.ts +230 -0
- package/tests/v1.2/chunked-upload.test.ts +301 -0
- package/tests/v1.2/cliff-detection.test.ts +272 -0
- package/tests/v1.2/commit-hash-system.test.ts +288 -0
- package/tests/v1.2/compression.test.ts +220 -0
- package/tests/v1.2/conflict-visualization.test.ts +263 -0
- package/tests/v1.2/distributed.test.ts +261 -0
- package/tests/v1.2/performance-monitoring.test.ts +328 -0
- package/tests/v1.3/auto-branching.test.ts +270 -0
- package/tests/v1.3/message-search.test.ts +264 -0
- package/tests/v1.3/stage-area.test.ts +330 -0
- package/tests/v1.3/stash-rebase-cherry-pick.test.ts +361 -0
- package/tests/v1.4/cli.test.ts +171 -0
- package/tests/v1.4/conflict-resolution-advanced.test.ts +429 -0
- package/tests/v1.4/conflict-resolution-ui.test.ts +286 -0
- package/tests/v1.4/workspace-isolation-advanced.test.ts +382 -0
- package/tests/v1.4/workspace-isolation.test.ts +268 -0
- package/tests/v1.5/agent-coordination.real.test.ts +401 -0
- package/tests/v1.5/cli-v2.test.ts +354 -0
- package/tests/v1.5/git-interop.real.test.ts +358 -0
- package/tests/v1.5/integration-testing.real.test.ts +440 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,440 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real Integration Testing - Trace v1.5.0
|
|
3
|
+
* Testing actual workflows with real agent scenarios
|
|
4
|
+
* This validates end-to-end functionality
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { TraceCliv2 } from '../../src/cli-v2';
|
|
8
|
+
import { WorkspaceIsolation } from '../../src/workspace-isolation';
|
|
9
|
+
import { AgentCoordination } from '../../src/agent-coordination';
|
|
10
|
+
import { ConflictResolutionUI } from '../../src/conflict-resolution-ui';
|
|
11
|
+
|
|
12
|
+
describe('Trace v1.5.0 - Real Integration Testing', () => {
|
|
13
|
+
let cli: TraceCliv2;
|
|
14
|
+
let isolation: WorkspaceIsolation;
|
|
15
|
+
let coordination: AgentCoordination;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
cli = new TraceCliv2();
|
|
19
|
+
isolation = new WorkspaceIsolation('/test/workspace', true);
|
|
20
|
+
coordination = new AgentCoordination({
|
|
21
|
+
allowParallel: true,
|
|
22
|
+
maxConcurrent: 10,
|
|
23
|
+
lockTimeout: 5000,
|
|
24
|
+
conflictStrategy: 'block',
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('Real Agent Workflow 1: Simple Collaborative Edit', () => {
|
|
29
|
+
test('two agents edit different files in parallel', async () => {
|
|
30
|
+
// Agent A starts
|
|
31
|
+
const initResult = await cli.execute('trace init /workspace');
|
|
32
|
+
expect(initResult.success).toBe(true);
|
|
33
|
+
|
|
34
|
+
// Agent A edits file 1
|
|
35
|
+
const lockA = await cli.execute('trace lock src/api.ts');
|
|
36
|
+
expect(lockA.success).toBe(true);
|
|
37
|
+
|
|
38
|
+
const commitA = await cli.execute('trace commit -m "feat: add user endpoint"');
|
|
39
|
+
expect(commitA.success).toBe(true);
|
|
40
|
+
|
|
41
|
+
// Agent B edits different file in parallel
|
|
42
|
+
const lockB = await cli.execute('trace lock src/utils.ts');
|
|
43
|
+
expect(lockB.success).toBe(true);
|
|
44
|
+
|
|
45
|
+
const commitB = await cli.execute('trace commit -m "refactor: optimize helpers"');
|
|
46
|
+
expect(commitB.success).toBe(true);
|
|
47
|
+
|
|
48
|
+
// Both unlock
|
|
49
|
+
const unlockA = await cli.execute('trace unlock src/api.ts');
|
|
50
|
+
const unlockB = await cli.execute('trace unlock src/utils.ts');
|
|
51
|
+
expect(unlockA.success).toBe(true);
|
|
52
|
+
expect(unlockB.success).toBe(true);
|
|
53
|
+
|
|
54
|
+
// Both push
|
|
55
|
+
const pushA = await cli.execute('trace push origin main');
|
|
56
|
+
const pushB = await cli.execute('trace push origin main');
|
|
57
|
+
expect(pushA.success).toBe(true);
|
|
58
|
+
expect(pushB.success).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('Real Agent Workflow 2: Serialized File Editing', () => {
|
|
63
|
+
test('two agents queue for same file, second waits', async () => {
|
|
64
|
+
// Agent A locks shared file
|
|
65
|
+
const taskA = coordination.registerTask('agent-a', ['src/shared.ts'], 2);
|
|
66
|
+
expect(taskA.canStart).toBe(true);
|
|
67
|
+
|
|
68
|
+
// Agent A gets lock
|
|
69
|
+
const lockA = coordination.requestLock('src/shared.ts', 'agent-a');
|
|
70
|
+
expect(lockA.locked).toBe(true);
|
|
71
|
+
|
|
72
|
+
// Agent B tries same file, gets blocked
|
|
73
|
+
const taskB = coordination.registerTask('agent-b', ['src/shared.ts'], 1);
|
|
74
|
+
expect(taskB.canStart).toBe(false);
|
|
75
|
+
expect(taskB.blockedBy).toContain(taskA.taskId);
|
|
76
|
+
|
|
77
|
+
// Agent A completes
|
|
78
|
+
const unlockA = coordination.releaseLock('src/shared.ts', 'agent-a');
|
|
79
|
+
expect(unlockA).toBe(true);
|
|
80
|
+
|
|
81
|
+
coordination.completeTask(taskA.taskId);
|
|
82
|
+
|
|
83
|
+
// Now Agent B should be able to proceed
|
|
84
|
+
const lockB = coordination.requestLock('src/shared.ts', 'agent-b');
|
|
85
|
+
expect(lockB.locked).toBe(true);
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('Real Agent Workflow 3: Conflict Detection & Resolution', () => {
|
|
90
|
+
test('detect conflict, resolve it, commit', async () => {
|
|
91
|
+
// Both agents try to edit same file
|
|
92
|
+
const taskA = coordination.registerTask('agent-a', ['src/main.ts', 'src/api.ts'], 2);
|
|
93
|
+
const taskB = coordination.registerTask('agent-b', ['src/main.ts', 'src/utils.ts'], 1);
|
|
94
|
+
|
|
95
|
+
// A can start
|
|
96
|
+
expect(taskA.canStart).toBe(true);
|
|
97
|
+
|
|
98
|
+
// B is blocked (conflict on main.ts)
|
|
99
|
+
expect(taskB.canStart).toBe(false);
|
|
100
|
+
|
|
101
|
+
// Detect collision
|
|
102
|
+
const collisions = coordination.getBlockedTasks();
|
|
103
|
+
expect(collisions.length).toBeGreaterThan(0);
|
|
104
|
+
|
|
105
|
+
// Show stats
|
|
106
|
+
const stats = coordination.getStats();
|
|
107
|
+
expect(stats.blocked).toBe(1);
|
|
108
|
+
expect(stats.executing).toBe(1);
|
|
109
|
+
|
|
110
|
+
// A completes
|
|
111
|
+
coordination.completeTask(taskA.taskId);
|
|
112
|
+
|
|
113
|
+
// B auto-unblocks
|
|
114
|
+
const unblocked = coordination.getBlockedTasks();
|
|
115
|
+
expect(unblocked.length).toBe(0);
|
|
116
|
+
|
|
117
|
+
// B can now proceed
|
|
118
|
+
const statusB = coordination.getTaskStatus(taskB.taskId);
|
|
119
|
+
expect(statusB?.status).toBe('executing');
|
|
120
|
+
|
|
121
|
+
// Resolve the conflict (if happened)
|
|
122
|
+
const resolveResult = await cli.execute('trace resolve src/main.ts --strategy ours');
|
|
123
|
+
expect(resolveResult.success).toBe(true);
|
|
124
|
+
|
|
125
|
+
// Commit after resolution
|
|
126
|
+
const commitResult = await cli.execute('trace commit -m "fix: resolved merge conflict"');
|
|
127
|
+
expect(commitResult.success).toBe(true);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('Real Agent Workflow 4: Security & Validation', () => {
|
|
132
|
+
test('prevent accidental .env commit, validate before push', async () => {
|
|
133
|
+
// Agent tries to commit .env (dangerous!)
|
|
134
|
+
const envCheck = isolation.isSafeToCommit('.env');
|
|
135
|
+
expect(envCheck.safe).toBe(false);
|
|
136
|
+
expect(envCheck.violations.length).toBeGreaterThan(0);
|
|
137
|
+
|
|
138
|
+
// Check the violation reason exists
|
|
139
|
+
expect(envCheck.violations[0].reason).toBeTruthy();
|
|
140
|
+
expect(envCheck.violations[0].reason.length).toBeGreaterThan(0);
|
|
141
|
+
|
|
142
|
+
// Try to commit safe file
|
|
143
|
+
const safeCheck = isolation.isSafeToCommit('src/api.ts');
|
|
144
|
+
expect(safeCheck.safe).toBe(true);
|
|
145
|
+
expect(safeCheck.violations.length).toBe(0);
|
|
146
|
+
|
|
147
|
+
// Validate multiple files before push
|
|
148
|
+
const files = ['src/api.ts', 'src/utils.ts', 'tests/api.test.ts'];
|
|
149
|
+
const pushValidation = isolation.validateGitPush(files);
|
|
150
|
+
expect(pushValidation.canPush).toBe(true);
|
|
151
|
+
|
|
152
|
+
// Try with dangerous files
|
|
153
|
+
const dangerousFiles = ['src/api.ts', '.env', 'secrets.json'];
|
|
154
|
+
const unsafeValidation = isolation.validateGitPush(dangerousFiles);
|
|
155
|
+
expect(unsafeValidation.canPush).toBe(false);
|
|
156
|
+
expect(unsafeValidation.violations.length).toBeGreaterThan(0);
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
describe('Real Agent Workflow 5: Complex Task Dependencies', () => {
|
|
161
|
+
test('three agents with task dependencies (A→B→C)', async () => {
|
|
162
|
+
// Agent A: works on types (Task 1)
|
|
163
|
+
const taskA = coordination.registerTask('agent-a', ['src/types.ts'], 3);
|
|
164
|
+
expect(taskA.canStart).toBe(true);
|
|
165
|
+
|
|
166
|
+
// Agent B: works on API, depends on types (Task 2)
|
|
167
|
+
const taskB = coordination.registerTask('agent-b', ['src/api.ts', 'src/types.ts'], 2);
|
|
168
|
+
expect(taskB.canStart).toBe(false); // Blocked by A
|
|
169
|
+
expect(taskB.blockedBy).toContain(taskA.taskId);
|
|
170
|
+
|
|
171
|
+
// Agent C: works on tests, depends on both (Task 3)
|
|
172
|
+
const taskC = coordination.registerTask('agent-c', ['src/types.ts', 'src/api.ts', 'tests/api.test.ts'], 1);
|
|
173
|
+
expect(taskC.canStart).toBe(false); // Blocked by A and B
|
|
174
|
+
expect(taskC.blockedBy?.length).toBeGreaterThanOrEqual(1);
|
|
175
|
+
|
|
176
|
+
// Get suggested order
|
|
177
|
+
const order = coordination.suggestOrder();
|
|
178
|
+
const aIdx = order.indexOf(taskA.taskId);
|
|
179
|
+
const bIdx = order.indexOf(taskB.taskId);
|
|
180
|
+
const cIdx = order.indexOf(taskC.taskId);
|
|
181
|
+
|
|
182
|
+
// A should come before B before C
|
|
183
|
+
expect(aIdx).toBeLessThan(bIdx);
|
|
184
|
+
expect(bIdx).toBeLessThan(cIdx);
|
|
185
|
+
|
|
186
|
+
// Complete A
|
|
187
|
+
const unblocked1 = coordination.completeTask(taskA.taskId);
|
|
188
|
+
expect(unblocked1.unblocked).toContain(taskB.taskId);
|
|
189
|
+
|
|
190
|
+
// Verify B is now executing
|
|
191
|
+
const statusB = coordination.getTaskStatus(taskB.taskId);
|
|
192
|
+
expect(statusB?.status).toBe('executing');
|
|
193
|
+
|
|
194
|
+
// Complete B
|
|
195
|
+
const unblocked2 = coordination.completeTask(taskB.taskId);
|
|
196
|
+
expect(unblocked2.unblocked).toContain(taskC.taskId);
|
|
197
|
+
|
|
198
|
+
// Now C is executing
|
|
199
|
+
const statusC = coordination.getTaskStatus(taskC.taskId);
|
|
200
|
+
expect(statusC?.status).toBe('executing');
|
|
201
|
+
|
|
202
|
+
// Complete C
|
|
203
|
+
const unblocked3 = coordination.completeTask(taskC.taskId);
|
|
204
|
+
expect(unblocked3.success).toBe(true);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
describe('Real Agent Workflow 6: High Concurrency Scenario', () => {
|
|
209
|
+
test('10 agents working simultaneously on different files', async () => {
|
|
210
|
+
const agents = Array.from({ length: 10 }, (_, i) => `agent-${i}`);
|
|
211
|
+
const files = Array.from({ length: 10 }, (_, i) => `src/module${i}.ts`);
|
|
212
|
+
|
|
213
|
+
// All agents register tasks simultaneously
|
|
214
|
+
const tasks = agents.map((agent, i) => {
|
|
215
|
+
return coordination.registerTask(agent, [files[i]], 10 - i);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// All should start (no conflicts)
|
|
219
|
+
tasks.forEach(task => {
|
|
220
|
+
expect(task.canStart).toBe(true);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// All should get locks
|
|
224
|
+
files.forEach((file, i) => {
|
|
225
|
+
const lock = coordination.requestLock(file, agents[i]);
|
|
226
|
+
expect(lock.locked).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
// Get stats
|
|
230
|
+
const stats = coordination.getStats();
|
|
231
|
+
expect(stats.executing).toBe(10);
|
|
232
|
+
expect(stats.blocked).toBe(0);
|
|
233
|
+
expect(stats.currentLocks).toBe(10);
|
|
234
|
+
|
|
235
|
+
// Complete all tasks
|
|
236
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
237
|
+
const result = coordination.completeTask(tasks[i].taskId);
|
|
238
|
+
expect(result.success).toBe(true);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// All should be done
|
|
242
|
+
const finalStats = coordination.getStats();
|
|
243
|
+
expect(finalStats.completed).toBe(10);
|
|
244
|
+
expect(finalStats.executing).toBe(0);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
describe('Real Agent Workflow 7: Conflict Resolution UI', () => {
|
|
249
|
+
test('render conflict and suggest resolution', async () => {
|
|
250
|
+
const ours = `export function getUserName(id: string) {
|
|
251
|
+
return db.query('SELECT name FROM users WHERE id = ?', [id]);
|
|
252
|
+
}`;
|
|
253
|
+
|
|
254
|
+
const theirs = `export async function getUserName(id: string): Promise<string> {
|
|
255
|
+
const result = await db.query('SELECT name FROM users WHERE id = ?', [id]);
|
|
256
|
+
return result[0].name;
|
|
257
|
+
}`;
|
|
258
|
+
|
|
259
|
+
const base = `export function getUserName(id: string) {
|
|
260
|
+
return db.query('SELECT name FROM users WHERE id = ?', [id]);
|
|
261
|
+
}`;
|
|
262
|
+
|
|
263
|
+
// Get suggestion
|
|
264
|
+
const suggestion = ConflictResolutionUI.suggestResolution(ours, theirs, base);
|
|
265
|
+
expect(suggestion.suggestion).toBeDefined();
|
|
266
|
+
expect(suggestion.confidence).toBeGreaterThan(0);
|
|
267
|
+
expect(suggestion.reason).toBeDefined();
|
|
268
|
+
|
|
269
|
+
// Render HTML UI
|
|
270
|
+
const html = ConflictResolutionUI.renderHTML('src/users.ts', ours, theirs, base);
|
|
271
|
+
expect(html).toContain('Conflict Resolution');
|
|
272
|
+
expect(html).toContain('getUserName');
|
|
273
|
+
|
|
274
|
+
// Render terminal UI
|
|
275
|
+
const terminal = ConflictResolutionUI.renderTerminal(
|
|
276
|
+
'src/users.ts',
|
|
277
|
+
ours.split('\n'),
|
|
278
|
+
theirs.split('\n'),
|
|
279
|
+
base.split('\n')
|
|
280
|
+
);
|
|
281
|
+
expect(terminal).toContain('RESOLUTION OPTIONS');
|
|
282
|
+
|
|
283
|
+
// Parse conflict markers (real git format)
|
|
284
|
+
const conflicted = `<<<<<<< HEAD
|
|
285
|
+
export function getUserName(id: string) {
|
|
286
|
+
return db.query('SELECT name FROM users WHERE id = ?', [id]);
|
|
287
|
+
}
|
|
288
|
+
=======
|
|
289
|
+
export async function getUserName(id: string): Promise<string> {
|
|
290
|
+
const result = await db.query('SELECT name FROM users WHERE id = ?', [id]);
|
|
291
|
+
return result[0].name;
|
|
292
|
+
}
|
|
293
|
+
>>>>>>> feature/async-queries`;
|
|
294
|
+
|
|
295
|
+
const parsed = ConflictResolutionUI.parseConflictMarkers(conflicted);
|
|
296
|
+
expect(parsed.hasConflicts).toBe(true);
|
|
297
|
+
expect(parsed.markers.length).toBeGreaterThan(0);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
describe('Real Agent Workflow 8: Multi-Agent Coordination State', () => {
|
|
302
|
+
test('complete workflow with coordination state display', async () => {
|
|
303
|
+
// Register 5 tasks with varying priorities
|
|
304
|
+
const task1 = coordination.registerTask('agent-1', ['file1.ts'], 5);
|
|
305
|
+
const task2 = coordination.registerTask('agent-2', ['file1.ts'], 4); // Blocks on 1
|
|
306
|
+
const task3 = coordination.registerTask('agent-3', ['file2.ts', 'file1.ts'], 3); // Blocks on 1&2
|
|
307
|
+
const task4 = coordination.registerTask('agent-4', ['file2.ts'], 2);
|
|
308
|
+
const task5 = coordination.registerTask('agent-5', ['file3.ts'], 1);
|
|
309
|
+
|
|
310
|
+
// Get current state
|
|
311
|
+
let state = coordination.formatState();
|
|
312
|
+
expect(state).toContain('AGENT COORDINATION STATE');
|
|
313
|
+
expect(state).toContain('BLOCKED TASKS');
|
|
314
|
+
|
|
315
|
+
// Get stats
|
|
316
|
+
let stats = coordination.getStats();
|
|
317
|
+
expect(stats.executing).toBeGreaterThan(0);
|
|
318
|
+
expect(stats.blocked).toBeGreaterThan(0);
|
|
319
|
+
|
|
320
|
+
// Complete task 1
|
|
321
|
+
coordination.completeTask(task1.taskId);
|
|
322
|
+
stats = coordination.getStats();
|
|
323
|
+
// Task 2 should unblock, so blocked count decreases
|
|
324
|
+
expect(stats.blocked).toBeLessThanOrEqual(2);
|
|
325
|
+
|
|
326
|
+
// Complete task 4
|
|
327
|
+
coordination.completeTask(task4.taskId);
|
|
328
|
+
stats = coordination.getStats();
|
|
329
|
+
|
|
330
|
+
// Complete remaining
|
|
331
|
+
coordination.completeTask(task2.taskId);
|
|
332
|
+
coordination.completeTask(task3.taskId);
|
|
333
|
+
coordination.completeTask(task5.taskId);
|
|
334
|
+
|
|
335
|
+
// All done
|
|
336
|
+
stats = coordination.getStats();
|
|
337
|
+
expect(stats.completed).toBe(5);
|
|
338
|
+
expect(stats.executing).toBe(0);
|
|
339
|
+
expect(stats.blocked).toBe(0);
|
|
340
|
+
|
|
341
|
+
// View final state
|
|
342
|
+
state = coordination.formatState();
|
|
343
|
+
expect(state).toContain('5 total');
|
|
344
|
+
});
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
describe('Performance Validation (Real Workloads)', () => {
|
|
348
|
+
test('process 100 tasks under 1 second', () => {
|
|
349
|
+
const start = Date.now();
|
|
350
|
+
|
|
351
|
+
for (let i = 0; i < 100; i++) {
|
|
352
|
+
const file = `file${i % 20}.ts`;
|
|
353
|
+
coordination.registerTask(`agent-${i % 10}`, [file], 100 - i);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const elapsed = Date.now() - start;
|
|
357
|
+
expect(elapsed).toBeLessThan(1000);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
test('detect conflicts in 100 tasks under 100ms', () => {
|
|
361
|
+
const tasks = [];
|
|
362
|
+
for (let i = 0; i < 100; i++) {
|
|
363
|
+
tasks.push(coordination.registerTask(`agent-${i % 5}`, [`file${i % 10}.ts`], i));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
const start = Date.now();
|
|
367
|
+
const blocked = coordination.getBlockedTasks();
|
|
368
|
+
const elapsed = Date.now() - start;
|
|
369
|
+
|
|
370
|
+
expect(elapsed).toBeLessThan(100);
|
|
371
|
+
// Check if there are blocked tasks (may be 0 if all different files)
|
|
372
|
+
expect(Array.isArray(blocked)).toBe(true);
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test('file lock operations under 1ms each', () => {
|
|
376
|
+
const start = Date.now();
|
|
377
|
+
|
|
378
|
+
for (let i = 0; i < 1000; i++) {
|
|
379
|
+
const file = `file${i % 50}.ts`;
|
|
380
|
+
const agent = `agent-${i % 10}`;
|
|
381
|
+
coordination.requestLock(file, agent);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const elapsed = Date.now() - start;
|
|
385
|
+
expect(elapsed).toBeLessThan(100); // 1000 operations < 100ms
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
describe('End-to-End Sanity Checks', () => {
|
|
390
|
+
test('CLI commands respond immediately', async () => {
|
|
391
|
+
const commands = [
|
|
392
|
+
'trace help',
|
|
393
|
+
'trace status',
|
|
394
|
+
'trace log',
|
|
395
|
+
'trace audit',
|
|
396
|
+
'trace validate',
|
|
397
|
+
];
|
|
398
|
+
|
|
399
|
+
for (const cmd of commands) {
|
|
400
|
+
const start = Date.now();
|
|
401
|
+
const result = await cli.execute(cmd);
|
|
402
|
+
const elapsed = Date.now() - start;
|
|
403
|
+
|
|
404
|
+
expect(result.success).toBe(true);
|
|
405
|
+
expect(elapsed).toBeLessThan(100); // <100ms per command
|
|
406
|
+
}
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test('security checks work', () => {
|
|
410
|
+
const dangerousFiles = ['.env', 'secrets.json', 'private.key', 'password.txt'];
|
|
411
|
+
const safeFiles = ['src/api.ts', 'tests/main.test.ts', 'docs/API.md'];
|
|
412
|
+
|
|
413
|
+
dangerousFiles.forEach(file => {
|
|
414
|
+
const result = isolation.isSafeToCommit(file);
|
|
415
|
+
expect(result.safe).toBe(false);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
safeFiles.forEach(file => {
|
|
419
|
+
const result = isolation.isSafeToCommit(file);
|
|
420
|
+
expect(result.safe).toBe(true);
|
|
421
|
+
});
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test('coordination prevents collisions', () => {
|
|
425
|
+
// Register A for file X
|
|
426
|
+
const a = coordination.registerTask('agent-a', ['shared.ts'], 2);
|
|
427
|
+
expect(a.canStart).toBe(true);
|
|
428
|
+
|
|
429
|
+
// Register B for same file
|
|
430
|
+
const b = coordination.registerTask('agent-b', ['shared.ts'], 1);
|
|
431
|
+
expect(b.canStart).toBe(false);
|
|
432
|
+
|
|
433
|
+
// Verify collision detected
|
|
434
|
+
expect(b.blockedBy).toContain(a.taskId);
|
|
435
|
+
|
|
436
|
+
// Cleanup
|
|
437
|
+
coordination.completeTask(a.taskId);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"forceConsistentCasingInFileNames": true,
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"declarationMap": true,
|
|
15
|
+
"sourceMap": true,
|
|
16
|
+
"noImplicitAny": true,
|
|
17
|
+
"strictNullChecks": true,
|
|
18
|
+
"strictFunctionTypes": true,
|
|
19
|
+
"strictBindCallApply": true,
|
|
20
|
+
"strictPropertyInitialization": true,
|
|
21
|
+
"noImplicitThis": true,
|
|
22
|
+
"alwaysStrict": true
|
|
23
|
+
},
|
|
24
|
+
"include": ["src/**/*"],
|
|
25
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
26
|
+
}
|