@sparkleideas/claims 3.0.0-alpha.8-patch.26 → 3.0.0-alpha.8-patch.28
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/dist/api/cli-commands.d.ts +84 -0
- package/dist/api/cli-commands.d.ts.map +1 -0
- package/dist/api/cli-commands.js +1223 -0
- package/dist/api/cli-commands.js.map +1 -0
- package/dist/api/cli-types.d.ts +82 -0
- package/dist/api/cli-types.d.ts.map +1 -0
- package/dist/api/cli-types.js +87 -0
- package/dist/api/cli-types.js.map +1 -0
- package/dist/api/index.d.ts +9 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/index.js +11 -0
- package/dist/api/index.js.map +1 -0
- package/dist/api/mcp-tools.d.ts +376 -0
- package/dist/api/mcp-tools.d.ts.map +1 -0
- package/dist/api/mcp-tools.js +1409 -0
- package/dist/api/mcp-tools.js.map +1 -0
- package/dist/application/claim-service.d.ts +99 -0
- package/dist/application/claim-service.d.ts.map +1 -0
- package/dist/application/claim-service.js +440 -0
- package/dist/application/claim-service.js.map +1 -0
- package/dist/application/index.d.ts +15 -0
- package/dist/application/index.d.ts.map +1 -0
- package/dist/application/index.js +17 -0
- package/dist/application/index.js.map +1 -0
- package/dist/application/load-balancer.d.ts +353 -0
- package/dist/application/load-balancer.d.ts.map +1 -0
- package/dist/application/load-balancer.js +430 -0
- package/dist/application/load-balancer.js.map +1 -0
- package/dist/application/work-stealing-service.d.ts +149 -0
- package/dist/application/work-stealing-service.d.ts.map +1 -0
- package/dist/application/work-stealing-service.js +604 -0
- package/dist/application/work-stealing-service.js.map +1 -0
- package/dist/domain/events.d.ts +308 -0
- package/dist/domain/events.d.ts.map +1 -0
- package/dist/domain/events.js +241 -0
- package/dist/domain/events.js.map +1 -0
- package/dist/domain/index.d.ts +16 -0
- package/dist/domain/index.d.ts.map +1 -0
- package/dist/domain/index.js +34 -0
- package/dist/domain/index.js.map +1 -0
- package/dist/domain/repositories.d.ts +154 -0
- package/dist/domain/repositories.d.ts.map +1 -0
- package/dist/domain/repositories.js +9 -0
- package/dist/domain/repositories.js.map +1 -0
- package/dist/domain/rules.d.ts +105 -0
- package/dist/domain/rules.d.ts.map +1 -0
- package/dist/domain/rules.js +348 -0
- package/dist/domain/rules.js.map +1 -0
- package/dist/domain/types.d.ts +578 -0
- package/dist/domain/types.d.ts.map +1 -0
- package/dist/domain/types.js +99 -0
- package/dist/domain/types.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/claim-repository.d.ts +46 -0
- package/dist/infrastructure/claim-repository.d.ts.map +1 -0
- package/dist/infrastructure/claim-repository.js +274 -0
- package/dist/infrastructure/claim-repository.js.map +1 -0
- package/dist/infrastructure/event-store.d.ts +62 -0
- package/dist/infrastructure/event-store.d.ts.map +1 -0
- package/dist/infrastructure/event-store.js +189 -0
- package/dist/infrastructure/event-store.js.map +1 -0
- package/dist/infrastructure/index.d.ts +10 -0
- package/dist/infrastructure/index.d.ts.map +1 -0
- package/dist/infrastructure/index.js +12 -0
- package/dist/infrastructure/index.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Repository Interfaces
|
|
3
|
+
*
|
|
4
|
+
* Repository interfaces for the claims domain following DDD patterns.
|
|
5
|
+
*
|
|
6
|
+
* @module v3/claims/domain/repositories
|
|
7
|
+
*/
|
|
8
|
+
import { ClaimId, IssueId, Claimant, ClaimStatus, Issue, IssueClaim, IssueFilters } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Repository for managing issue claims
|
|
11
|
+
*/
|
|
12
|
+
export interface IClaimRepository {
|
|
13
|
+
/**
|
|
14
|
+
* Save a new claim or update an existing one
|
|
15
|
+
*/
|
|
16
|
+
save(claim: IssueClaim): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Find a claim by its ID
|
|
19
|
+
*/
|
|
20
|
+
findById(claimId: ClaimId): Promise<IssueClaim | null>;
|
|
21
|
+
/**
|
|
22
|
+
* Find the active claim for an issue
|
|
23
|
+
*/
|
|
24
|
+
findByIssueId(issueId: IssueId): Promise<IssueClaim | null>;
|
|
25
|
+
/**
|
|
26
|
+
* Find all claims for a specific claimant
|
|
27
|
+
*/
|
|
28
|
+
findByClaimant(claimant: Claimant): Promise<IssueClaim[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Find claims by status
|
|
31
|
+
*/
|
|
32
|
+
findByStatus(status: ClaimStatus): Promise<IssueClaim[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Delete a claim
|
|
35
|
+
*/
|
|
36
|
+
delete(claimId: ClaimId): Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* Find all active claims
|
|
39
|
+
*/
|
|
40
|
+
findActiveClaims(): Promise<IssueClaim[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Find stale claims (claims with no activity past a threshold)
|
|
43
|
+
*/
|
|
44
|
+
findStaleClaims(staleSince: Date): Promise<IssueClaim[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Find claims with pending handoffs
|
|
47
|
+
*/
|
|
48
|
+
findClaimsWithPendingHandoffs(): Promise<IssueClaim[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Count claims by claimant
|
|
51
|
+
*/
|
|
52
|
+
countByClaimant(claimantId: string): Promise<number>;
|
|
53
|
+
/**
|
|
54
|
+
* Initialize the repository
|
|
55
|
+
*/
|
|
56
|
+
initialize(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Shutdown the repository
|
|
59
|
+
*/
|
|
60
|
+
shutdown(): Promise<void>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Repository for accessing issues
|
|
64
|
+
*/
|
|
65
|
+
export interface IIssueRepository {
|
|
66
|
+
/**
|
|
67
|
+
* Find an issue by its ID
|
|
68
|
+
*/
|
|
69
|
+
findById(issueId: IssueId): Promise<Issue | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Find issues matching filters
|
|
72
|
+
*/
|
|
73
|
+
findByFilters(filters: IssueFilters): Promise<Issue[]>;
|
|
74
|
+
/**
|
|
75
|
+
* Find all unclaimed issues matching filters
|
|
76
|
+
*/
|
|
77
|
+
findAvailable(filters?: IssueFilters): Promise<Issue[]>;
|
|
78
|
+
/**
|
|
79
|
+
* Check if an issue exists
|
|
80
|
+
*/
|
|
81
|
+
exists(issueId: IssueId): Promise<boolean>;
|
|
82
|
+
/**
|
|
83
|
+
* Initialize the repository
|
|
84
|
+
*/
|
|
85
|
+
initialize(): Promise<void>;
|
|
86
|
+
/**
|
|
87
|
+
* Shutdown the repository
|
|
88
|
+
*/
|
|
89
|
+
shutdown(): Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Repository for managing claimants
|
|
93
|
+
*/
|
|
94
|
+
export interface IClaimantRepository {
|
|
95
|
+
/**
|
|
96
|
+
* Find a claimant by ID
|
|
97
|
+
*/
|
|
98
|
+
findById(claimantId: string): Promise<Claimant | null>;
|
|
99
|
+
/**
|
|
100
|
+
* Find claimants by type
|
|
101
|
+
*/
|
|
102
|
+
findByType(type: 'human' | 'agent'): Promise<Claimant[]>;
|
|
103
|
+
/**
|
|
104
|
+
* Find claimants with specific capabilities
|
|
105
|
+
*/
|
|
106
|
+
findByCapabilities(capabilities: string[]): Promise<Claimant[]>;
|
|
107
|
+
/**
|
|
108
|
+
* Get all available claimants (not at max workload)
|
|
109
|
+
*/
|
|
110
|
+
findAvailable(): Promise<Claimant[]>;
|
|
111
|
+
/**
|
|
112
|
+
* Check if a claimant exists
|
|
113
|
+
*/
|
|
114
|
+
exists(claimantId: string): Promise<boolean>;
|
|
115
|
+
/**
|
|
116
|
+
* Initialize the repository
|
|
117
|
+
*/
|
|
118
|
+
initialize(): Promise<void>;
|
|
119
|
+
/**
|
|
120
|
+
* Shutdown the repository
|
|
121
|
+
*/
|
|
122
|
+
shutdown(): Promise<void>;
|
|
123
|
+
}
|
|
124
|
+
import { ClaimDomainEvent } from './events.js';
|
|
125
|
+
/**
|
|
126
|
+
* Event store interface for claim domain events
|
|
127
|
+
*/
|
|
128
|
+
export interface IClaimEventStore {
|
|
129
|
+
/**
|
|
130
|
+
* Append a new event to the store
|
|
131
|
+
*/
|
|
132
|
+
append(event: ClaimDomainEvent): Promise<void>;
|
|
133
|
+
/**
|
|
134
|
+
* Get events for a specific claim
|
|
135
|
+
*/
|
|
136
|
+
getEvents(claimId: ClaimId, fromVersion?: number): Promise<ClaimDomainEvent[]>;
|
|
137
|
+
/**
|
|
138
|
+
* Get events by type
|
|
139
|
+
*/
|
|
140
|
+
getEventsByType(type: string): Promise<ClaimDomainEvent[]>;
|
|
141
|
+
/**
|
|
142
|
+
* Get events for an issue across all claims
|
|
143
|
+
*/
|
|
144
|
+
getEventsByIssueId(issueId: IssueId): Promise<ClaimDomainEvent[]>;
|
|
145
|
+
/**
|
|
146
|
+
* Initialize the event store
|
|
147
|
+
*/
|
|
148
|
+
initialize(): Promise<void>;
|
|
149
|
+
/**
|
|
150
|
+
* Shutdown the event store
|
|
151
|
+
*/
|
|
152
|
+
shutdown(): Promise<void>;
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=repositories.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.d.ts","sourceRoot":"","sources":["../../src/domain/repositories.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repositories.js","sourceRoot":"","sources":["../../src/domain/repositories.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sparkleideas/claims - Business Rules (ADR-016)
|
|
3
|
+
* Domain rules for claiming, stealing eligibility, and load balancing
|
|
4
|
+
*
|
|
5
|
+
* Pure functions that encode the business logic for the claiming system
|
|
6
|
+
*/
|
|
7
|
+
import type { IssueClaim, Claimant, ClaimStatus, IssuePriority, WorkStealingConfig, IssueClaimWithStealing, StealableReason, ExtendedClaimStatus } from './types.js';
|
|
8
|
+
export interface RuleResult<T = boolean> {
|
|
9
|
+
success: boolean;
|
|
10
|
+
data?: T;
|
|
11
|
+
error?: {
|
|
12
|
+
code: string;
|
|
13
|
+
message: string;
|
|
14
|
+
details?: Record<string, unknown>;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function ruleSuccess<T>(data: T): RuleResult<T>;
|
|
18
|
+
export declare function ruleFailure(code: string, message: string, details?: Record<string, unknown>): RuleResult<never>;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a claimant can claim a new issue
|
|
21
|
+
*/
|
|
22
|
+
export declare function canClaimIssue(claimant: Claimant, existingClaims: readonly IssueClaim[]): RuleResult<boolean>;
|
|
23
|
+
/**
|
|
24
|
+
* Check if an issue is already claimed
|
|
25
|
+
*/
|
|
26
|
+
export declare function isIssueClaimed(issueId: string, claims: readonly IssueClaim[]): IssueClaim | null;
|
|
27
|
+
/**
|
|
28
|
+
* Determine if a claim status is considered "active"
|
|
29
|
+
*/
|
|
30
|
+
export declare function isActiveClaim(status: ClaimStatus | ExtendedClaimStatus): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Get valid status transitions for original ClaimStatus
|
|
33
|
+
*/
|
|
34
|
+
export declare function getOriginalStatusTransitions(currentStatus: ClaimStatus): readonly ClaimStatus[];
|
|
35
|
+
/**
|
|
36
|
+
* Get valid status transitions for ExtendedClaimStatus (ADR-016)
|
|
37
|
+
*/
|
|
38
|
+
export declare function getExtendedStatusTransitions(currentStatus: ExtendedClaimStatus): readonly ExtendedClaimStatus[];
|
|
39
|
+
/**
|
|
40
|
+
* Check if a status transition is valid
|
|
41
|
+
*/
|
|
42
|
+
export declare function canTransitionStatus(currentStatus: ClaimStatus | ExtendedClaimStatus, newStatus: ClaimStatus | ExtendedClaimStatus): RuleResult<boolean>;
|
|
43
|
+
/**
|
|
44
|
+
* Check if a claim is eligible to be marked as stealable
|
|
45
|
+
*/
|
|
46
|
+
export declare function canMarkAsStealable(claim: IssueClaimWithStealing, config: WorkStealingConfig, now?: Date): RuleResult<StealableReason | null>;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a claim can be stolen by a specific agent
|
|
49
|
+
*/
|
|
50
|
+
export declare function canStealClaim(claim: IssueClaimWithStealing, challenger: Claimant, config: WorkStealingConfig, now?: Date): RuleResult<boolean>;
|
|
51
|
+
/**
|
|
52
|
+
* Determine if a contest is required for stealing
|
|
53
|
+
*/
|
|
54
|
+
export declare function requiresStealContest(claim: IssueClaimWithStealing, config: WorkStealingConfig): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Check if a handoff can be initiated
|
|
57
|
+
*/
|
|
58
|
+
export declare function canInitiateHandoff(claim: IssueClaim, targetClaimant: Claimant, currentClaimant: Claimant): RuleResult<boolean>;
|
|
59
|
+
/**
|
|
60
|
+
* Check if a handoff can be accepted
|
|
61
|
+
*/
|
|
62
|
+
export declare function canAcceptHandoff(claim: IssueClaim, acceptingClaimant: Claimant): RuleResult<boolean>;
|
|
63
|
+
/**
|
|
64
|
+
* Check if a handoff can be rejected
|
|
65
|
+
*/
|
|
66
|
+
export declare function canRejectHandoff(claim: IssueClaim, rejectingClaimant: Claimant): RuleResult<boolean>;
|
|
67
|
+
/**
|
|
68
|
+
* Determine if an agent is overloaded
|
|
69
|
+
*/
|
|
70
|
+
export declare function isAgentOverloaded(load: number, threshold?: number): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* Determine if an agent is underloaded
|
|
73
|
+
*/
|
|
74
|
+
export declare function isAgentUnderloaded(load: number, threshold?: number): boolean;
|
|
75
|
+
/**
|
|
76
|
+
* Check if rebalancing is needed for a set of agents
|
|
77
|
+
*/
|
|
78
|
+
export declare function needsRebalancing(agentLoads: readonly {
|
|
79
|
+
load: number;
|
|
80
|
+
}[], config: {
|
|
81
|
+
overloadThreshold: number;
|
|
82
|
+
underloadThreshold: number;
|
|
83
|
+
rebalanceThreshold: number;
|
|
84
|
+
}): boolean;
|
|
85
|
+
/**
|
|
86
|
+
* Check if a claim can be moved during rebalancing
|
|
87
|
+
*/
|
|
88
|
+
export declare function canMoveClaim(claim: IssueClaimWithStealing): boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Validate claim priority
|
|
91
|
+
*/
|
|
92
|
+
export declare function isValidPriority(priority: string): priority is IssuePriority;
|
|
93
|
+
/**
|
|
94
|
+
* Validate claim status
|
|
95
|
+
*/
|
|
96
|
+
export declare function isValidStatus(status: string): status is ClaimStatus;
|
|
97
|
+
/**
|
|
98
|
+
* Validate extended claim status (ADR-016)
|
|
99
|
+
*/
|
|
100
|
+
export declare function isValidExtendedStatus(status: string): status is ExtendedClaimStatus;
|
|
101
|
+
/**
|
|
102
|
+
* Validate repository format (owner/repo)
|
|
103
|
+
*/
|
|
104
|
+
export declare function isValidRepository(repository: string): boolean;
|
|
105
|
+
//# sourceMappingURL=rules.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/domain/rules.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @sparkleideas/claims - Business Rules (ADR-016)
|
|
3
|
+
* Domain rules for claiming, stealing eligibility, and load balancing
|
|
4
|
+
*
|
|
5
|
+
* Pure functions that encode the business logic for the claiming system
|
|
6
|
+
*/
|
|
7
|
+
export function ruleSuccess(data) {
|
|
8
|
+
return { success: true, data };
|
|
9
|
+
}
|
|
10
|
+
export function ruleFailure(code, message, details) {
|
|
11
|
+
return { success: false, error: { code, message, details } };
|
|
12
|
+
}
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Claim Eligibility Rules
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Check if a claimant can claim a new issue
|
|
18
|
+
*/
|
|
19
|
+
export function canClaimIssue(claimant, existingClaims) {
|
|
20
|
+
// Check capacity
|
|
21
|
+
const maxClaims = claimant.maxConcurrentClaims ?? 5;
|
|
22
|
+
const activeClaims = existingClaims.filter((c) => c.claimant.id === claimant.id && isActiveClaim(c.status)).length;
|
|
23
|
+
if (activeClaims >= maxClaims) {
|
|
24
|
+
return ruleFailure('CLAIMANT_AT_CAPACITY', `Claimant has reached maximum concurrent claims (${maxClaims})`, { currentClaims: activeClaims, maxClaims });
|
|
25
|
+
}
|
|
26
|
+
// Check workload if available
|
|
27
|
+
const workload = claimant.currentWorkload ?? 0;
|
|
28
|
+
if (workload >= 100) {
|
|
29
|
+
return ruleFailure('CLAIMANT_AT_CAPACITY', 'Claimant is at 100% capacity', { workload });
|
|
30
|
+
}
|
|
31
|
+
return ruleSuccess(true);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Check if an issue is already claimed
|
|
35
|
+
*/
|
|
36
|
+
export function isIssueClaimed(issueId, claims) {
|
|
37
|
+
return claims.find((c) => c.issueId === issueId && isActiveClaim(c.status)) ?? null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Determine if a claim status is considered "active"
|
|
41
|
+
*/
|
|
42
|
+
export function isActiveClaim(status) {
|
|
43
|
+
return [
|
|
44
|
+
'active',
|
|
45
|
+
'paused',
|
|
46
|
+
'blocked',
|
|
47
|
+
'pending_handoff',
|
|
48
|
+
'handoff-pending',
|
|
49
|
+
'in_review',
|
|
50
|
+
'review-requested',
|
|
51
|
+
].includes(status);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get valid status transitions for original ClaimStatus
|
|
55
|
+
*/
|
|
56
|
+
export function getOriginalStatusTransitions(currentStatus) {
|
|
57
|
+
const transitions = {
|
|
58
|
+
'active': ['pending_handoff', 'in_review', 'completed', 'released', 'paused', 'blocked', 'stealable'],
|
|
59
|
+
'pending_handoff': ['active', 'completed'],
|
|
60
|
+
'in_review': ['active', 'completed'],
|
|
61
|
+
'completed': [],
|
|
62
|
+
'released': [],
|
|
63
|
+
'expired': [],
|
|
64
|
+
'paused': ['active', 'blocked', 'stealable', 'completed'],
|
|
65
|
+
'blocked': ['active', 'paused', 'stealable', 'completed'],
|
|
66
|
+
'stealable': ['active', 'completed'],
|
|
67
|
+
};
|
|
68
|
+
return transitions[currentStatus] ?? [];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get valid status transitions for ExtendedClaimStatus (ADR-016)
|
|
72
|
+
*/
|
|
73
|
+
export function getExtendedStatusTransitions(currentStatus) {
|
|
74
|
+
const transitions = {
|
|
75
|
+
'active': ['paused', 'blocked', 'handoff-pending', 'review-requested', 'stealable', 'completed'],
|
|
76
|
+
'paused': ['active', 'blocked', 'handoff-pending', 'stealable', 'completed'],
|
|
77
|
+
'blocked': ['active', 'paused', 'stealable', 'completed'],
|
|
78
|
+
'handoff-pending': ['active', 'completed'],
|
|
79
|
+
'review-requested': ['active', 'completed', 'blocked'],
|
|
80
|
+
'stealable': ['active', 'completed'],
|
|
81
|
+
'completed': [],
|
|
82
|
+
};
|
|
83
|
+
return transitions[currentStatus];
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Check if a status transition is valid
|
|
87
|
+
*/
|
|
88
|
+
export function canTransitionStatus(currentStatus, newStatus) {
|
|
89
|
+
if (currentStatus === newStatus) {
|
|
90
|
+
return ruleSuccess(true); // No-op is always valid
|
|
91
|
+
}
|
|
92
|
+
// Try original transitions first
|
|
93
|
+
const originalTransitions = getOriginalStatusTransitions(currentStatus);
|
|
94
|
+
if (originalTransitions.includes(newStatus)) {
|
|
95
|
+
return ruleSuccess(true);
|
|
96
|
+
}
|
|
97
|
+
// Try extended transitions
|
|
98
|
+
const extendedTransitions = getExtendedStatusTransitions(currentStatus);
|
|
99
|
+
if (extendedTransitions.includes(newStatus)) {
|
|
100
|
+
return ruleSuccess(true);
|
|
101
|
+
}
|
|
102
|
+
return ruleFailure('INVALID_STATUS_TRANSITION', `Cannot transition from '${currentStatus}' to '${newStatus}'`, { currentStatus, newStatus });
|
|
103
|
+
}
|
|
104
|
+
// =============================================================================
|
|
105
|
+
// Work Stealing Rules
|
|
106
|
+
// =============================================================================
|
|
107
|
+
/**
|
|
108
|
+
* Check if a claim is eligible to be marked as stealable
|
|
109
|
+
*/
|
|
110
|
+
export function canMarkAsStealable(claim, config, now = new Date()) {
|
|
111
|
+
// Already stealable or terminal status
|
|
112
|
+
if (claim.status === 'stealable' || claim.status === 'completed' || claim.status === 'released') {
|
|
113
|
+
return ruleSuccess(null);
|
|
114
|
+
}
|
|
115
|
+
// Check for stale (no activity)
|
|
116
|
+
const lastActivity = claim.lastActivityAt.getTime();
|
|
117
|
+
const staleThreshold = config.staleThresholdMinutes * 60 * 1000;
|
|
118
|
+
if (now.getTime() - lastActivity >= staleThreshold) {
|
|
119
|
+
return ruleSuccess('stale');
|
|
120
|
+
}
|
|
121
|
+
// Check for blocked too long
|
|
122
|
+
if (claim.blockedAt && claim.blockedReason) {
|
|
123
|
+
const blockedThreshold = config.blockedThresholdMinutes * 60 * 1000;
|
|
124
|
+
if (now.getTime() - claim.blockedAt.getTime() >= blockedThreshold) {
|
|
125
|
+
return ruleSuccess('blocked');
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return ruleSuccess(null);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if a claim can be stolen by a specific agent
|
|
132
|
+
*/
|
|
133
|
+
export function canStealClaim(claim, challenger, config, now = new Date()) {
|
|
134
|
+
// Check if claim is stealable
|
|
135
|
+
if (claim.status !== 'stealable' && !claim.stealInfo) {
|
|
136
|
+
return ruleFailure('NOT_STEALABLE', `Claim status is '${claim.status}', not stealable`);
|
|
137
|
+
}
|
|
138
|
+
// Check grace period
|
|
139
|
+
if (claim.stealableAt && now < claim.stealableAt) {
|
|
140
|
+
return ruleFailure('IN_GRACE_PERIOD', `Grace period has not ended. Ends at ${claim.stealableAt.toISOString()}`, { stealableAt: claim.stealableAt.getTime() });
|
|
141
|
+
}
|
|
142
|
+
// Check progress protection
|
|
143
|
+
if (claim.progress >= config.minProgressToProtect) {
|
|
144
|
+
return ruleFailure('PROTECTED_BY_PROGRESS', `Claim is protected due to high progress (${claim.progress}%)`, { progress: claim.progress, threshold: config.minProgressToProtect });
|
|
145
|
+
}
|
|
146
|
+
// Check cross-type stealing rules if applicable
|
|
147
|
+
if (config.allowCrossTypeSteal && claim.stealInfo?.allowedStealerTypes) {
|
|
148
|
+
const challengerType = challenger.agentType;
|
|
149
|
+
if (challengerType && !claim.stealInfo.allowedStealerTypes.includes(challengerType)) {
|
|
150
|
+
return ruleFailure('CROSS_TYPE_NOT_ALLOWED', `Agent type '${challengerType}' cannot steal from this claim`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Cannot steal own claim
|
|
154
|
+
if (claim.claimant.id === challenger.id) {
|
|
155
|
+
return ruleFailure('UNAUTHORIZED', 'Cannot steal your own claim');
|
|
156
|
+
}
|
|
157
|
+
// Check if there's a pending contest
|
|
158
|
+
if (claim.contestInfo && !claim.contestInfo.resolution) {
|
|
159
|
+
return ruleFailure('CONTEST_PENDING', 'A steal contest is already in progress');
|
|
160
|
+
}
|
|
161
|
+
return ruleSuccess(true);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Determine if a contest is required for stealing
|
|
165
|
+
*/
|
|
166
|
+
export function requiresStealContest(claim, config) {
|
|
167
|
+
// No contest for timeout/stale claims
|
|
168
|
+
if (claim.stealInfo?.reason === 'stale' || claim.stealInfo?.reason === 'timeout') {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
// No contest for manual releases
|
|
172
|
+
if (claim.stealInfo?.reason === 'manual') {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
// Contest required based on progress
|
|
176
|
+
return claim.progress > 0;
|
|
177
|
+
}
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// Handoff Rules
|
|
180
|
+
// =============================================================================
|
|
181
|
+
/**
|
|
182
|
+
* Check if a handoff can be initiated
|
|
183
|
+
*/
|
|
184
|
+
export function canInitiateHandoff(claim, targetClaimant, currentClaimant) {
|
|
185
|
+
// Cannot handoff completed claims
|
|
186
|
+
if (claim.status === 'completed' || claim.status === 'released') {
|
|
187
|
+
return ruleFailure('INVALID_STATUS', 'Cannot hand off a completed or released claim');
|
|
188
|
+
}
|
|
189
|
+
// Cannot handoff if already pending
|
|
190
|
+
if (claim.status === 'pending_handoff') {
|
|
191
|
+
return ruleFailure('HANDOFF_PENDING', 'A handoff is already pending for this claim');
|
|
192
|
+
}
|
|
193
|
+
// Must be the current claimant
|
|
194
|
+
if (claim.claimant.id !== currentClaimant.id) {
|
|
195
|
+
return ruleFailure('UNAUTHORIZED', 'Only the current claimant can initiate a handoff');
|
|
196
|
+
}
|
|
197
|
+
// Cannot handoff to self
|
|
198
|
+
if (claim.claimant.id === targetClaimant.id) {
|
|
199
|
+
return ruleFailure('VALIDATION_ERROR', 'Cannot hand off to yourself');
|
|
200
|
+
}
|
|
201
|
+
// Check target capacity
|
|
202
|
+
const targetMaxClaims = targetClaimant.maxConcurrentClaims ?? 5;
|
|
203
|
+
const targetWorkload = targetClaimant.currentWorkload ?? 0;
|
|
204
|
+
if (targetWorkload >= 100) {
|
|
205
|
+
return ruleFailure('TARGET_AT_CAPACITY', 'Target claimant is at full capacity');
|
|
206
|
+
}
|
|
207
|
+
return ruleSuccess(true);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Check if a handoff can be accepted
|
|
211
|
+
*/
|
|
212
|
+
export function canAcceptHandoff(claim, acceptingClaimant) {
|
|
213
|
+
if (claim.status !== 'pending_handoff') {
|
|
214
|
+
return ruleFailure('INVALID_STATUS', 'Claim is not in pending handoff status');
|
|
215
|
+
}
|
|
216
|
+
// Find the pending handoff record
|
|
217
|
+
const pendingHandoff = claim.handoffChain?.find(h => h.status === 'pending');
|
|
218
|
+
if (!pendingHandoff) {
|
|
219
|
+
return ruleFailure('HANDOFF_NOT_FOUND', 'No pending handoff found');
|
|
220
|
+
}
|
|
221
|
+
if (pendingHandoff.to.id !== acceptingClaimant.id) {
|
|
222
|
+
return ruleFailure('UNAUTHORIZED', 'Only the target claimant can accept this handoff');
|
|
223
|
+
}
|
|
224
|
+
return ruleSuccess(true);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Check if a handoff can be rejected
|
|
228
|
+
*/
|
|
229
|
+
export function canRejectHandoff(claim, rejectingClaimant) {
|
|
230
|
+
if (claim.status !== 'pending_handoff') {
|
|
231
|
+
return ruleFailure('INVALID_STATUS', 'Claim is not in pending handoff status');
|
|
232
|
+
}
|
|
233
|
+
const pendingHandoff = claim.handoffChain?.find(h => h.status === 'pending');
|
|
234
|
+
if (!pendingHandoff) {
|
|
235
|
+
return ruleFailure('HANDOFF_NOT_FOUND', 'No pending handoff found');
|
|
236
|
+
}
|
|
237
|
+
// Either the target or the initiator can reject
|
|
238
|
+
const canReject = pendingHandoff.to.id === rejectingClaimant.id ||
|
|
239
|
+
pendingHandoff.from.id === rejectingClaimant.id;
|
|
240
|
+
if (!canReject) {
|
|
241
|
+
return ruleFailure('UNAUTHORIZED', 'Only the target or initiating claimant can reject this handoff');
|
|
242
|
+
}
|
|
243
|
+
return ruleSuccess(true);
|
|
244
|
+
}
|
|
245
|
+
// =============================================================================
|
|
246
|
+
// Load Balancing Rules
|
|
247
|
+
// =============================================================================
|
|
248
|
+
/**
|
|
249
|
+
* Determine if an agent is overloaded
|
|
250
|
+
*/
|
|
251
|
+
export function isAgentOverloaded(load, threshold = 90) {
|
|
252
|
+
return load >= threshold;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Determine if an agent is underloaded
|
|
256
|
+
*/
|
|
257
|
+
export function isAgentUnderloaded(load, threshold = 30) {
|
|
258
|
+
return load <= threshold;
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Check if rebalancing is needed for a set of agents
|
|
262
|
+
*/
|
|
263
|
+
export function needsRebalancing(agentLoads, config) {
|
|
264
|
+
if (agentLoads.length < 2) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
// Check for overloaded agents
|
|
268
|
+
const hasOverloaded = agentLoads.some((a) => isAgentOverloaded(a.load, config.overloadThreshold));
|
|
269
|
+
const hasUnderloaded = agentLoads.some((a) => isAgentUnderloaded(a.load, config.underloadThreshold));
|
|
270
|
+
if (hasOverloaded && hasUnderloaded) {
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
273
|
+
// Check for large load differential
|
|
274
|
+
const loads = agentLoads.map((a) => a.load);
|
|
275
|
+
const maxLoad = Math.max(...loads);
|
|
276
|
+
const minLoad = Math.min(...loads);
|
|
277
|
+
const loadDifferential = maxLoad - minLoad;
|
|
278
|
+
return loadDifferential >= config.rebalanceThreshold;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Check if a claim can be moved during rebalancing
|
|
282
|
+
*/
|
|
283
|
+
export function canMoveClaim(claim) {
|
|
284
|
+
// Cannot move completed claims
|
|
285
|
+
if (claim.status === 'completed' || claim.status === 'released') {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
// Cannot move claims with pending handoffs
|
|
289
|
+
if (claim.status === 'pending_handoff') {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
// Cannot move high-progress claims (>75%)
|
|
293
|
+
if (claim.progress > 75) {
|
|
294
|
+
return false;
|
|
295
|
+
}
|
|
296
|
+
// Cannot move claims with active reviews
|
|
297
|
+
if (claim.status === 'in_review') {
|
|
298
|
+
return false;
|
|
299
|
+
}
|
|
300
|
+
// Cannot move contested claims
|
|
301
|
+
if (claim.contestInfo && !claim.contestInfo.resolution) {
|
|
302
|
+
return false;
|
|
303
|
+
}
|
|
304
|
+
return true;
|
|
305
|
+
}
|
|
306
|
+
// =============================================================================
|
|
307
|
+
// Validation Rules
|
|
308
|
+
// =============================================================================
|
|
309
|
+
/**
|
|
310
|
+
* Validate claim priority
|
|
311
|
+
*/
|
|
312
|
+
export function isValidPriority(priority) {
|
|
313
|
+
return ['critical', 'high', 'medium', 'low'].includes(priority);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Validate claim status
|
|
317
|
+
*/
|
|
318
|
+
export function isValidStatus(status) {
|
|
319
|
+
return [
|
|
320
|
+
'active',
|
|
321
|
+
'pending_handoff',
|
|
322
|
+
'in_review',
|
|
323
|
+
'completed',
|
|
324
|
+
'released',
|
|
325
|
+
'expired',
|
|
326
|
+
].includes(status);
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Validate extended claim status (ADR-016)
|
|
330
|
+
*/
|
|
331
|
+
export function isValidExtendedStatus(status) {
|
|
332
|
+
return [
|
|
333
|
+
'active',
|
|
334
|
+
'paused',
|
|
335
|
+
'handoff-pending',
|
|
336
|
+
'review-requested',
|
|
337
|
+
'blocked',
|
|
338
|
+
'stealable',
|
|
339
|
+
'completed',
|
|
340
|
+
].includes(status);
|
|
341
|
+
}
|
|
342
|
+
/**
|
|
343
|
+
* Validate repository format (owner/repo)
|
|
344
|
+
*/
|
|
345
|
+
export function isValidRepository(repository) {
|
|
346
|
+
return /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/.test(repository);
|
|
347
|
+
}
|
|
348
|
+
//# sourceMappingURL=rules.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rules.js","sourceRoot":"","sources":["../../src/domain/rules.ts"],"names":[],"mappings":"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"}
|