@sparkleideas/claims 3.5.2-patch.1
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/README.md +261 -0
- package/package.json +84 -0
- package/src/api/cli-commands.ts +1459 -0
- package/src/api/cli-types.ts +154 -0
- package/src/api/index.ts +24 -0
- package/src/api/mcp-tools.ts +1977 -0
- package/src/application/claim-service.ts +753 -0
- package/src/application/index.ts +46 -0
- package/src/application/load-balancer.ts +840 -0
- package/src/application/work-stealing-service.ts +807 -0
- package/src/domain/events.ts +779 -0
- package/src/domain/index.ts +214 -0
- package/src/domain/repositories.ts +239 -0
- package/src/domain/rules.ts +526 -0
- package/src/domain/types.ts +826 -0
- package/src/index.ts +79 -0
- package/src/infrastructure/claim-repository.ts +358 -0
- package/src/infrastructure/event-store.ts +297 -0
- package/src/infrastructure/index.ts +21 -0
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Domain Events (ADR-007)
|
|
3
|
+
*
|
|
4
|
+
* Domain events for the claims system following event sourcing pattern.
|
|
5
|
+
* All state changes emit events for audit trail and projections.
|
|
6
|
+
*
|
|
7
|
+
* @module v3/claims/domain/events
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type {
|
|
11
|
+
ClaimId,
|
|
12
|
+
IssueId,
|
|
13
|
+
Claimant,
|
|
14
|
+
ClaimStatus,
|
|
15
|
+
} from './types.js';
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// Base Claim Event
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Base interface for all claim domain events
|
|
23
|
+
*/
|
|
24
|
+
export interface ClaimDomainEvent {
|
|
25
|
+
/** Unique event identifier */
|
|
26
|
+
id: string;
|
|
27
|
+
|
|
28
|
+
/** Event type discriminator */
|
|
29
|
+
type: ClaimEventType;
|
|
30
|
+
|
|
31
|
+
/** Aggregate ID (claim ID) */
|
|
32
|
+
aggregateId: string;
|
|
33
|
+
|
|
34
|
+
/** Aggregate type - always 'claim' for this domain */
|
|
35
|
+
aggregateType: 'claim';
|
|
36
|
+
|
|
37
|
+
/** Event version for ordering */
|
|
38
|
+
version: number;
|
|
39
|
+
|
|
40
|
+
/** Timestamp when event occurred */
|
|
41
|
+
timestamp: number;
|
|
42
|
+
|
|
43
|
+
/** Event source */
|
|
44
|
+
source: string;
|
|
45
|
+
|
|
46
|
+
/** Event payload data */
|
|
47
|
+
payload: Record<string, unknown>;
|
|
48
|
+
|
|
49
|
+
/** Optional metadata */
|
|
50
|
+
metadata?: Record<string, unknown>;
|
|
51
|
+
|
|
52
|
+
/** Optional causation ID (event that caused this event) */
|
|
53
|
+
causationId?: string;
|
|
54
|
+
|
|
55
|
+
/** Optional correlation ID (groups related events) */
|
|
56
|
+
correlationId?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// =============================================================================
|
|
60
|
+
// Event Types
|
|
61
|
+
// =============================================================================
|
|
62
|
+
|
|
63
|
+
export type ClaimEventType =
|
|
64
|
+
| 'claim:created'
|
|
65
|
+
| 'claim:released'
|
|
66
|
+
| 'claim:expired'
|
|
67
|
+
| 'claim:status-changed'
|
|
68
|
+
| 'claim:note-added'
|
|
69
|
+
| 'handoff:requested'
|
|
70
|
+
| 'handoff:accepted'
|
|
71
|
+
| 'handoff:rejected'
|
|
72
|
+
| 'review:requested'
|
|
73
|
+
| 'review:completed';
|
|
74
|
+
|
|
75
|
+
// =============================================================================
|
|
76
|
+
// Specific Event Interfaces
|
|
77
|
+
// =============================================================================
|
|
78
|
+
|
|
79
|
+
export interface ClaimCreatedEvent extends ClaimDomainEvent {
|
|
80
|
+
type: 'claim:created';
|
|
81
|
+
payload: {
|
|
82
|
+
claimId: ClaimId;
|
|
83
|
+
issueId: IssueId;
|
|
84
|
+
claimant: Claimant;
|
|
85
|
+
claimedAt: number;
|
|
86
|
+
expiresAt?: number;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface ClaimReleasedEvent extends ClaimDomainEvent {
|
|
91
|
+
type: 'claim:released';
|
|
92
|
+
payload: {
|
|
93
|
+
claimId: ClaimId;
|
|
94
|
+
issueId: IssueId;
|
|
95
|
+
claimant: Claimant;
|
|
96
|
+
releasedAt: number;
|
|
97
|
+
reason?: string;
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface ClaimExpiredEvent extends ClaimDomainEvent {
|
|
102
|
+
type: 'claim:expired';
|
|
103
|
+
payload: {
|
|
104
|
+
claimId: ClaimId;
|
|
105
|
+
issueId: IssueId;
|
|
106
|
+
claimant: Claimant;
|
|
107
|
+
expiredAt: number;
|
|
108
|
+
lastActivityAt: number;
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface ClaimStatusChangedEvent extends ClaimDomainEvent {
|
|
113
|
+
type: 'claim:status-changed';
|
|
114
|
+
payload: {
|
|
115
|
+
claimId: ClaimId;
|
|
116
|
+
issueId: IssueId;
|
|
117
|
+
previousStatus: ClaimStatus;
|
|
118
|
+
newStatus: ClaimStatus;
|
|
119
|
+
changedAt: number;
|
|
120
|
+
note?: string;
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface ClaimNoteAddedEvent extends ClaimDomainEvent {
|
|
125
|
+
type: 'claim:note-added';
|
|
126
|
+
payload: {
|
|
127
|
+
claimId: ClaimId;
|
|
128
|
+
issueId: IssueId;
|
|
129
|
+
note: string;
|
|
130
|
+
addedAt: number;
|
|
131
|
+
addedBy: Claimant;
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export interface HandoffRequestedEvent extends ClaimDomainEvent {
|
|
136
|
+
type: 'handoff:requested';
|
|
137
|
+
payload: {
|
|
138
|
+
claimId: ClaimId;
|
|
139
|
+
issueId: IssueId;
|
|
140
|
+
handoffId: string;
|
|
141
|
+
from: Claimant;
|
|
142
|
+
to: Claimant;
|
|
143
|
+
reason: string;
|
|
144
|
+
requestedAt: number;
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export interface HandoffAcceptedEvent extends ClaimDomainEvent {
|
|
149
|
+
type: 'handoff:accepted';
|
|
150
|
+
payload: {
|
|
151
|
+
claimId: ClaimId;
|
|
152
|
+
issueId: IssueId;
|
|
153
|
+
handoffId: string;
|
|
154
|
+
from: Claimant;
|
|
155
|
+
to: Claimant;
|
|
156
|
+
acceptedAt: number;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface HandoffRejectedEvent extends ClaimDomainEvent {
|
|
161
|
+
type: 'handoff:rejected';
|
|
162
|
+
payload: {
|
|
163
|
+
claimId: ClaimId;
|
|
164
|
+
issueId: IssueId;
|
|
165
|
+
handoffId: string;
|
|
166
|
+
from: Claimant;
|
|
167
|
+
to: Claimant;
|
|
168
|
+
rejectedAt: number;
|
|
169
|
+
reason: string;
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export interface ReviewRequestedEvent extends ClaimDomainEvent {
|
|
174
|
+
type: 'review:requested';
|
|
175
|
+
payload: {
|
|
176
|
+
claimId: ClaimId;
|
|
177
|
+
issueId: IssueId;
|
|
178
|
+
reviewers: Claimant[];
|
|
179
|
+
requestedAt: number;
|
|
180
|
+
requestedBy: Claimant;
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export interface ReviewCompletedEvent extends ClaimDomainEvent {
|
|
185
|
+
type: 'review:completed';
|
|
186
|
+
payload: {
|
|
187
|
+
claimId: ClaimId;
|
|
188
|
+
issueId: IssueId;
|
|
189
|
+
reviewer: Claimant;
|
|
190
|
+
approved: boolean;
|
|
191
|
+
completedAt: number;
|
|
192
|
+
comments?: string;
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// =============================================================================
|
|
197
|
+
// Event Type Union
|
|
198
|
+
// =============================================================================
|
|
199
|
+
|
|
200
|
+
export type AllClaimEvents =
|
|
201
|
+
| ClaimCreatedEvent
|
|
202
|
+
| ClaimReleasedEvent
|
|
203
|
+
| ClaimExpiredEvent
|
|
204
|
+
| ClaimStatusChangedEvent
|
|
205
|
+
| ClaimNoteAddedEvent
|
|
206
|
+
| HandoffRequestedEvent
|
|
207
|
+
| HandoffAcceptedEvent
|
|
208
|
+
| HandoffRejectedEvent
|
|
209
|
+
| ReviewRequestedEvent
|
|
210
|
+
| ReviewCompletedEvent;
|
|
211
|
+
|
|
212
|
+
// =============================================================================
|
|
213
|
+
// Event Factory Functions
|
|
214
|
+
// =============================================================================
|
|
215
|
+
|
|
216
|
+
let eventCounter = 0;
|
|
217
|
+
|
|
218
|
+
function generateEventId(): string {
|
|
219
|
+
return `claim-evt-${Date.now()}-${++eventCounter}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function createClaimEvent<T extends ClaimDomainEvent>(
|
|
223
|
+
type: T['type'],
|
|
224
|
+
aggregateId: string,
|
|
225
|
+
payload: T['payload'],
|
|
226
|
+
metadata?: Record<string, unknown>,
|
|
227
|
+
causationId?: string,
|
|
228
|
+
correlationId?: string
|
|
229
|
+
): T {
|
|
230
|
+
return {
|
|
231
|
+
id: generateEventId(),
|
|
232
|
+
type,
|
|
233
|
+
aggregateId,
|
|
234
|
+
aggregateType: 'claim',
|
|
235
|
+
version: 1, // Version will be set by event store
|
|
236
|
+
timestamp: Date.now(),
|
|
237
|
+
source: 'claim-service',
|
|
238
|
+
payload,
|
|
239
|
+
metadata,
|
|
240
|
+
causationId,
|
|
241
|
+
correlationId,
|
|
242
|
+
} as T;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// =============================================================================
|
|
246
|
+
// Public Event Factory Functions
|
|
247
|
+
// =============================================================================
|
|
248
|
+
|
|
249
|
+
export function createClaimCreatedEvent(
|
|
250
|
+
claimId: ClaimId,
|
|
251
|
+
issueId: IssueId,
|
|
252
|
+
claimant: Claimant,
|
|
253
|
+
expiresAt?: number
|
|
254
|
+
): ClaimCreatedEvent {
|
|
255
|
+
return createClaimEvent('claim:created', claimId, {
|
|
256
|
+
claimId,
|
|
257
|
+
issueId,
|
|
258
|
+
claimant,
|
|
259
|
+
claimedAt: Date.now(),
|
|
260
|
+
expiresAt,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export function createClaimReleasedEvent(
|
|
265
|
+
claimId: ClaimId,
|
|
266
|
+
issueId: IssueId,
|
|
267
|
+
claimant: Claimant,
|
|
268
|
+
reason?: string
|
|
269
|
+
): ClaimReleasedEvent {
|
|
270
|
+
return createClaimEvent('claim:released', claimId, {
|
|
271
|
+
claimId,
|
|
272
|
+
issueId,
|
|
273
|
+
claimant,
|
|
274
|
+
releasedAt: Date.now(),
|
|
275
|
+
reason,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function createClaimExpiredEvent(
|
|
280
|
+
claimId: ClaimId,
|
|
281
|
+
issueId: IssueId,
|
|
282
|
+
claimant: Claimant,
|
|
283
|
+
lastActivityAt: number
|
|
284
|
+
): ClaimExpiredEvent {
|
|
285
|
+
return createClaimEvent('claim:expired', claimId, {
|
|
286
|
+
claimId,
|
|
287
|
+
issueId,
|
|
288
|
+
claimant,
|
|
289
|
+
expiredAt: Date.now(),
|
|
290
|
+
lastActivityAt,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export function createClaimStatusChangedEvent(
|
|
295
|
+
claimId: ClaimId,
|
|
296
|
+
issueId: IssueId,
|
|
297
|
+
previousStatus: ClaimStatus,
|
|
298
|
+
newStatus: ClaimStatus,
|
|
299
|
+
note?: string
|
|
300
|
+
): ClaimStatusChangedEvent {
|
|
301
|
+
return createClaimEvent('claim:status-changed', claimId, {
|
|
302
|
+
claimId,
|
|
303
|
+
issueId,
|
|
304
|
+
previousStatus,
|
|
305
|
+
newStatus,
|
|
306
|
+
changedAt: Date.now(),
|
|
307
|
+
note,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
export function createClaimNoteAddedEvent(
|
|
312
|
+
claimId: ClaimId,
|
|
313
|
+
issueId: IssueId,
|
|
314
|
+
note: string,
|
|
315
|
+
addedBy: Claimant
|
|
316
|
+
): ClaimNoteAddedEvent {
|
|
317
|
+
return createClaimEvent('claim:note-added', claimId, {
|
|
318
|
+
claimId,
|
|
319
|
+
issueId,
|
|
320
|
+
note,
|
|
321
|
+
addedAt: Date.now(),
|
|
322
|
+
addedBy,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
export function createHandoffRequestedEvent(
|
|
327
|
+
claimId: ClaimId,
|
|
328
|
+
issueId: IssueId,
|
|
329
|
+
handoffId: string,
|
|
330
|
+
from: Claimant,
|
|
331
|
+
to: Claimant,
|
|
332
|
+
reason: string
|
|
333
|
+
): HandoffRequestedEvent {
|
|
334
|
+
return createClaimEvent('handoff:requested', claimId, {
|
|
335
|
+
claimId,
|
|
336
|
+
issueId,
|
|
337
|
+
handoffId,
|
|
338
|
+
from,
|
|
339
|
+
to,
|
|
340
|
+
reason,
|
|
341
|
+
requestedAt: Date.now(),
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function createHandoffAcceptedEvent(
|
|
346
|
+
claimId: ClaimId,
|
|
347
|
+
issueId: IssueId,
|
|
348
|
+
handoffId: string,
|
|
349
|
+
from: Claimant,
|
|
350
|
+
to: Claimant
|
|
351
|
+
): HandoffAcceptedEvent {
|
|
352
|
+
return createClaimEvent('handoff:accepted', claimId, {
|
|
353
|
+
claimId,
|
|
354
|
+
issueId,
|
|
355
|
+
handoffId,
|
|
356
|
+
from,
|
|
357
|
+
to,
|
|
358
|
+
acceptedAt: Date.now(),
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export function createHandoffRejectedEvent(
|
|
363
|
+
claimId: ClaimId,
|
|
364
|
+
issueId: IssueId,
|
|
365
|
+
handoffId: string,
|
|
366
|
+
from: Claimant,
|
|
367
|
+
to: Claimant,
|
|
368
|
+
reason: string
|
|
369
|
+
): HandoffRejectedEvent {
|
|
370
|
+
return createClaimEvent('handoff:rejected', claimId, {
|
|
371
|
+
claimId,
|
|
372
|
+
issueId,
|
|
373
|
+
handoffId,
|
|
374
|
+
from,
|
|
375
|
+
to,
|
|
376
|
+
rejectedAt: Date.now(),
|
|
377
|
+
reason,
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
export function createReviewRequestedEvent(
|
|
382
|
+
claimId: ClaimId,
|
|
383
|
+
issueId: IssueId,
|
|
384
|
+
reviewers: Claimant[],
|
|
385
|
+
requestedBy: Claimant
|
|
386
|
+
): ReviewRequestedEvent {
|
|
387
|
+
return createClaimEvent('review:requested', claimId, {
|
|
388
|
+
claimId,
|
|
389
|
+
issueId,
|
|
390
|
+
reviewers,
|
|
391
|
+
requestedAt: Date.now(),
|
|
392
|
+
requestedBy,
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
export function createReviewCompletedEvent(
|
|
397
|
+
claimId: ClaimId,
|
|
398
|
+
issueId: IssueId,
|
|
399
|
+
reviewer: Claimant,
|
|
400
|
+
approved: boolean,
|
|
401
|
+
comments?: string
|
|
402
|
+
): ReviewCompletedEvent {
|
|
403
|
+
return createClaimEvent('review:completed', claimId, {
|
|
404
|
+
claimId,
|
|
405
|
+
issueId,
|
|
406
|
+
reviewer,
|
|
407
|
+
approved,
|
|
408
|
+
completedAt: Date.now(),
|
|
409
|
+
comments,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// =============================================================================
|
|
414
|
+
// ADR-016 Extended Events
|
|
415
|
+
// =============================================================================
|
|
416
|
+
|
|
417
|
+
import type {
|
|
418
|
+
AgentId,
|
|
419
|
+
StealReason,
|
|
420
|
+
AgentLoadInfo,
|
|
421
|
+
ClaimMove,
|
|
422
|
+
} from './types.js';
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Extended event types for ADR-016
|
|
426
|
+
*/
|
|
427
|
+
export type ExtendedClaimEventType =
|
|
428
|
+
| ClaimEventType
|
|
429
|
+
// Work stealing events
|
|
430
|
+
| 'steal:issue-marked-stealable'
|
|
431
|
+
| 'steal:issue-stolen'
|
|
432
|
+
| 'steal:contest-started'
|
|
433
|
+
| 'steal:contest-resolved'
|
|
434
|
+
| 'steal:warning-sent'
|
|
435
|
+
// Load balancing events
|
|
436
|
+
| 'swarm:rebalanced'
|
|
437
|
+
| 'agent:overloaded'
|
|
438
|
+
| 'agent:underloaded'
|
|
439
|
+
| 'agent:load-changed';
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Extended base event interface for ADR-016 events
|
|
443
|
+
*/
|
|
444
|
+
export interface ExtendedClaimDomainEvent {
|
|
445
|
+
/** Unique event identifier */
|
|
446
|
+
id: string;
|
|
447
|
+
|
|
448
|
+
/** Event type discriminator */
|
|
449
|
+
type: ExtendedClaimEventType;
|
|
450
|
+
|
|
451
|
+
/** Aggregate ID (claim ID) */
|
|
452
|
+
aggregateId: string;
|
|
453
|
+
|
|
454
|
+
/** Aggregate type - always 'claim' for this domain */
|
|
455
|
+
aggregateType: 'claim';
|
|
456
|
+
|
|
457
|
+
/** Event version for ordering */
|
|
458
|
+
version: number;
|
|
459
|
+
|
|
460
|
+
/** Timestamp when event occurred */
|
|
461
|
+
timestamp: number;
|
|
462
|
+
|
|
463
|
+
/** Event source */
|
|
464
|
+
source: string;
|
|
465
|
+
|
|
466
|
+
/** Event payload data */
|
|
467
|
+
payload: Record<string, unknown>;
|
|
468
|
+
|
|
469
|
+
/** Optional metadata */
|
|
470
|
+
metadata?: Record<string, unknown>;
|
|
471
|
+
|
|
472
|
+
/** Optional causation ID (event that caused this event) */
|
|
473
|
+
causationId?: string;
|
|
474
|
+
|
|
475
|
+
/** Optional correlation ID (groups related events) */
|
|
476
|
+
correlationId?: string;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// =============================================================================
|
|
480
|
+
// Work Stealing Events (ADR-016)
|
|
481
|
+
// =============================================================================
|
|
482
|
+
|
|
483
|
+
export interface IssueMarkedStealableEvent extends ExtendedClaimDomainEvent {
|
|
484
|
+
type: 'steal:issue-marked-stealable';
|
|
485
|
+
payload: {
|
|
486
|
+
claimId: ClaimId;
|
|
487
|
+
issueId: IssueId;
|
|
488
|
+
originalClaimant: Claimant;
|
|
489
|
+
reason: StealReason;
|
|
490
|
+
gracePeriodMs: number;
|
|
491
|
+
gracePeriodEndsAt: number;
|
|
492
|
+
minPriorityToSteal: string;
|
|
493
|
+
requiresContest: boolean;
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
export interface IssueStolenEvent extends ExtendedClaimDomainEvent {
|
|
498
|
+
type: 'steal:issue-stolen';
|
|
499
|
+
payload: {
|
|
500
|
+
claimId: ClaimId;
|
|
501
|
+
issueId: IssueId;
|
|
502
|
+
originalClaimant: Claimant;
|
|
503
|
+
newClaimant: Claimant;
|
|
504
|
+
reason: StealReason;
|
|
505
|
+
hadContest: boolean;
|
|
506
|
+
contestId?: string;
|
|
507
|
+
progressTransferred: number;
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export interface StealContestStartedEvent extends ExtendedClaimDomainEvent {
|
|
512
|
+
type: 'steal:contest-started';
|
|
513
|
+
payload: {
|
|
514
|
+
contestId: string;
|
|
515
|
+
claimId: ClaimId;
|
|
516
|
+
issueId: IssueId;
|
|
517
|
+
defender: Claimant;
|
|
518
|
+
challenger: Claimant;
|
|
519
|
+
reason: StealReason;
|
|
520
|
+
endsAt: number;
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
export interface StealContestResolvedExtEvent extends ExtendedClaimDomainEvent {
|
|
525
|
+
type: 'steal:contest-resolved';
|
|
526
|
+
payload: {
|
|
527
|
+
contestId: string;
|
|
528
|
+
claimId: ClaimId;
|
|
529
|
+
issueId: IssueId;
|
|
530
|
+
winner: 'defender' | 'challenger';
|
|
531
|
+
winnerClaimant: Claimant;
|
|
532
|
+
loserClaimant: Claimant;
|
|
533
|
+
resolvedBy: AgentId | 'system';
|
|
534
|
+
reason: string;
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export interface StealWarningEvent extends ExtendedClaimDomainEvent {
|
|
539
|
+
type: 'steal:warning-sent';
|
|
540
|
+
payload: {
|
|
541
|
+
claimId: ClaimId;
|
|
542
|
+
issueId: IssueId;
|
|
543
|
+
claimant: Claimant;
|
|
544
|
+
reason: StealReason;
|
|
545
|
+
warningNumber: number;
|
|
546
|
+
maxWarnings: number;
|
|
547
|
+
stealableAt: number;
|
|
548
|
+
actionRequired: string;
|
|
549
|
+
};
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// =============================================================================
|
|
553
|
+
// Load Balancing Events (ADR-016)
|
|
554
|
+
// =============================================================================
|
|
555
|
+
|
|
556
|
+
export interface SwarmRebalancedExtEvent extends ExtendedClaimDomainEvent {
|
|
557
|
+
type: 'swarm:rebalanced';
|
|
558
|
+
payload: {
|
|
559
|
+
claimsMoved: number;
|
|
560
|
+
moves: ClaimMove[];
|
|
561
|
+
loadBefore: AgentLoadInfo[];
|
|
562
|
+
loadAfter: AgentLoadInfo[];
|
|
563
|
+
durationMs: number;
|
|
564
|
+
trigger: 'scheduled' | 'overload-detected' | 'underload-detected' | 'manual' | 'agent-added' | 'agent-removed';
|
|
565
|
+
errors: string[];
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export interface AgentOverloadedExtEvent extends ExtendedClaimDomainEvent {
|
|
570
|
+
type: 'agent:overloaded';
|
|
571
|
+
payload: {
|
|
572
|
+
agentId: AgentId;
|
|
573
|
+
agentName: string;
|
|
574
|
+
currentLoad: number;
|
|
575
|
+
threshold: number;
|
|
576
|
+
activeClaims: number;
|
|
577
|
+
maxClaims: number;
|
|
578
|
+
recommendedAction: 'pause-assignments' | 'rebalance' | 'scale-up' | 'notify-admin';
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export interface AgentUnderloadedExtEvent extends ExtendedClaimDomainEvent {
|
|
583
|
+
type: 'agent:underloaded';
|
|
584
|
+
payload: {
|
|
585
|
+
agentId: AgentId;
|
|
586
|
+
agentName: string;
|
|
587
|
+
currentLoad: number;
|
|
588
|
+
threshold: number;
|
|
589
|
+
activeClaims: number;
|
|
590
|
+
maxClaims: number;
|
|
591
|
+
availableCapacity: number;
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export interface AgentLoadChangedEvent extends ExtendedClaimDomainEvent {
|
|
596
|
+
type: 'agent:load-changed';
|
|
597
|
+
payload: {
|
|
598
|
+
agentId: AgentId;
|
|
599
|
+
previousLoad: number;
|
|
600
|
+
currentLoad: number;
|
|
601
|
+
previousClaims: number;
|
|
602
|
+
currentClaims: number;
|
|
603
|
+
changeReason: 'claim-added' | 'claim-completed' | 'claim-released' | 'claim-transferred' | 'capacity-changed';
|
|
604
|
+
};
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* All ADR-016 extended events union
|
|
609
|
+
*/
|
|
610
|
+
export type AllExtendedClaimEvents =
|
|
611
|
+
| AllClaimEvents
|
|
612
|
+
| IssueMarkedStealableEvent
|
|
613
|
+
| IssueStolenEvent
|
|
614
|
+
| StealContestStartedEvent
|
|
615
|
+
| StealContestResolvedExtEvent
|
|
616
|
+
| StealWarningEvent
|
|
617
|
+
| SwarmRebalancedExtEvent
|
|
618
|
+
| AgentOverloadedExtEvent
|
|
619
|
+
| AgentUnderloadedExtEvent
|
|
620
|
+
| AgentLoadChangedEvent;
|
|
621
|
+
|
|
622
|
+
// =============================================================================
|
|
623
|
+
// Extended Event Factory Functions
|
|
624
|
+
// =============================================================================
|
|
625
|
+
|
|
626
|
+
export function createIssueMarkedStealableEvent(
|
|
627
|
+
claimId: ClaimId,
|
|
628
|
+
issueId: IssueId,
|
|
629
|
+
originalClaimant: Claimant,
|
|
630
|
+
reason: StealReason,
|
|
631
|
+
gracePeriodMs: number,
|
|
632
|
+
minPriorityToSteal: string,
|
|
633
|
+
requiresContest: boolean
|
|
634
|
+
): IssueMarkedStealableEvent {
|
|
635
|
+
const now = Date.now();
|
|
636
|
+
return {
|
|
637
|
+
id: generateExtEventId(),
|
|
638
|
+
type: 'steal:issue-marked-stealable',
|
|
639
|
+
aggregateId: claimId,
|
|
640
|
+
aggregateType: 'claim',
|
|
641
|
+
version: 1,
|
|
642
|
+
timestamp: now,
|
|
643
|
+
source: 'work-stealing-service',
|
|
644
|
+
payload: {
|
|
645
|
+
claimId,
|
|
646
|
+
issueId,
|
|
647
|
+
originalClaimant,
|
|
648
|
+
reason,
|
|
649
|
+
gracePeriodMs,
|
|
650
|
+
gracePeriodEndsAt: now + gracePeriodMs,
|
|
651
|
+
minPriorityToSteal,
|
|
652
|
+
requiresContest,
|
|
653
|
+
},
|
|
654
|
+
};
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
export function createIssueStolenExtEvent(
|
|
658
|
+
claimId: ClaimId,
|
|
659
|
+
issueId: IssueId,
|
|
660
|
+
originalClaimant: Claimant,
|
|
661
|
+
newClaimant: Claimant,
|
|
662
|
+
reason: StealReason,
|
|
663
|
+
hadContest: boolean,
|
|
664
|
+
progressTransferred: number,
|
|
665
|
+
contestId?: string
|
|
666
|
+
): IssueStolenEvent {
|
|
667
|
+
return {
|
|
668
|
+
id: generateExtEventId(),
|
|
669
|
+
type: 'steal:issue-stolen',
|
|
670
|
+
aggregateId: claimId,
|
|
671
|
+
aggregateType: 'claim',
|
|
672
|
+
version: 1,
|
|
673
|
+
timestamp: Date.now(),
|
|
674
|
+
source: 'work-stealing-service',
|
|
675
|
+
payload: {
|
|
676
|
+
claimId,
|
|
677
|
+
issueId,
|
|
678
|
+
originalClaimant,
|
|
679
|
+
newClaimant,
|
|
680
|
+
reason,
|
|
681
|
+
hadContest,
|
|
682
|
+
contestId,
|
|
683
|
+
progressTransferred,
|
|
684
|
+
},
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
export function createSwarmRebalancedExtEvent(
|
|
689
|
+
claimsMoved: number,
|
|
690
|
+
moves: ClaimMove[],
|
|
691
|
+
loadBefore: AgentLoadInfo[],
|
|
692
|
+
loadAfter: AgentLoadInfo[],
|
|
693
|
+
durationMs: number,
|
|
694
|
+
trigger: SwarmRebalancedExtEvent['payload']['trigger'],
|
|
695
|
+
errors: string[] = []
|
|
696
|
+
): SwarmRebalancedExtEvent {
|
|
697
|
+
return {
|
|
698
|
+
id: generateExtEventId(),
|
|
699
|
+
type: 'swarm:rebalanced',
|
|
700
|
+
aggregateId: 'swarm',
|
|
701
|
+
aggregateType: 'claim',
|
|
702
|
+
version: 1,
|
|
703
|
+
timestamp: Date.now(),
|
|
704
|
+
source: 'load-balancer',
|
|
705
|
+
payload: {
|
|
706
|
+
claimsMoved,
|
|
707
|
+
moves,
|
|
708
|
+
loadBefore,
|
|
709
|
+
loadAfter,
|
|
710
|
+
durationMs,
|
|
711
|
+
trigger,
|
|
712
|
+
errors,
|
|
713
|
+
},
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
export function createAgentOverloadedExtEvent(
|
|
718
|
+
agentId: AgentId,
|
|
719
|
+
agentName: string,
|
|
720
|
+
currentLoad: number,
|
|
721
|
+
threshold: number,
|
|
722
|
+
activeClaims: number,
|
|
723
|
+
maxClaims: number,
|
|
724
|
+
recommendedAction: AgentOverloadedExtEvent['payload']['recommendedAction']
|
|
725
|
+
): AgentOverloadedExtEvent {
|
|
726
|
+
return {
|
|
727
|
+
id: generateExtEventId(),
|
|
728
|
+
type: 'agent:overloaded',
|
|
729
|
+
aggregateId: agentId,
|
|
730
|
+
aggregateType: 'claim',
|
|
731
|
+
version: 1,
|
|
732
|
+
timestamp: Date.now(),
|
|
733
|
+
source: 'load-balancer',
|
|
734
|
+
payload: {
|
|
735
|
+
agentId,
|
|
736
|
+
agentName,
|
|
737
|
+
currentLoad,
|
|
738
|
+
threshold,
|
|
739
|
+
activeClaims,
|
|
740
|
+
maxClaims,
|
|
741
|
+
recommendedAction,
|
|
742
|
+
},
|
|
743
|
+
};
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export function createAgentUnderloadedExtEvent(
|
|
747
|
+
agentId: AgentId,
|
|
748
|
+
agentName: string,
|
|
749
|
+
currentLoad: number,
|
|
750
|
+
threshold: number,
|
|
751
|
+
activeClaims: number,
|
|
752
|
+
maxClaims: number,
|
|
753
|
+
availableCapacity: number
|
|
754
|
+
): AgentUnderloadedExtEvent {
|
|
755
|
+
return {
|
|
756
|
+
id: generateExtEventId(),
|
|
757
|
+
type: 'agent:underloaded',
|
|
758
|
+
aggregateId: agentId,
|
|
759
|
+
aggregateType: 'claim',
|
|
760
|
+
version: 1,
|
|
761
|
+
timestamp: Date.now(),
|
|
762
|
+
source: 'load-balancer',
|
|
763
|
+
payload: {
|
|
764
|
+
agentId,
|
|
765
|
+
agentName,
|
|
766
|
+
currentLoad,
|
|
767
|
+
threshold,
|
|
768
|
+
activeClaims,
|
|
769
|
+
maxClaims,
|
|
770
|
+
availableCapacity,
|
|
771
|
+
},
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
let extEventCounter = 0;
|
|
776
|
+
|
|
777
|
+
function generateExtEventId(): string {
|
|
778
|
+
return `claim-ext-evt-${Date.now()}-${++extEventCounter}`;
|
|
779
|
+
}
|