cmp-standards 2.6.0 → 2.8.0-alpha
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 +633 -633
- package/dist/cache/EmbeddingCache.d.ts +109 -0
- package/dist/cache/EmbeddingCache.d.ts.map +1 -0
- package/dist/cache/EmbeddingCache.js +239 -0
- package/dist/cache/EmbeddingCache.js.map +1 -0
- package/dist/cache/index.d.ts +6 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +6 -0
- package/dist/cache/index.js.map +1 -0
- package/dist/db/turso-client.js +11 -11
- package/dist/eslint/rules/no-async-useeffect.js +6 -6
- package/dist/events/EventBus.d.ts +87 -0
- package/dist/events/EventBus.d.ts.map +1 -0
- package/dist/events/EventBus.js +200 -0
- package/dist/events/EventBus.js.map +1 -0
- package/dist/events/index.d.ts +7 -0
- package/dist/events/index.d.ts.map +1 -0
- package/dist/events/index.js +9 -0
- package/dist/events/index.js.map +1 -0
- package/dist/events/types.d.ts +989 -0
- package/dist/events/types.d.ts.map +1 -0
- package/dist/events/types.js +136 -0
- package/dist/events/types.js.map +1 -0
- package/dist/experts/ConsensusEngine.d.ts +57 -0
- package/dist/experts/ConsensusEngine.d.ts.map +1 -0
- package/dist/experts/ConsensusEngine.js +146 -0
- package/dist/experts/ConsensusEngine.js.map +1 -0
- package/dist/experts/ExpertPanelService.d.ts +84 -0
- package/dist/experts/ExpertPanelService.d.ts.map +1 -0
- package/dist/experts/ExpertPanelService.js +204 -0
- package/dist/experts/ExpertPanelService.js.map +1 -0
- package/dist/experts/ExpertRouter.d.ts +68 -0
- package/dist/experts/ExpertRouter.d.ts.map +1 -0
- package/dist/experts/ExpertRouter.js +374 -0
- package/dist/experts/ExpertRouter.js.map +1 -0
- package/dist/experts/VoteCollector.d.ts +58 -0
- package/dist/experts/VoteCollector.d.ts.map +1 -0
- package/dist/experts/VoteCollector.js +146 -0
- package/dist/experts/VoteCollector.js.map +1 -0
- package/dist/experts/index.d.ts +9 -0
- package/dist/experts/index.d.ts.map +1 -0
- package/dist/experts/index.js +13 -0
- package/dist/experts/index.js.map +1 -0
- package/dist/hooks/cloud-pre-tool-use.js +20 -20
- package/dist/hooks/expert-review.d.ts +74 -0
- package/dist/hooks/expert-review.d.ts.map +1 -0
- package/dist/hooks/expert-review.js +220 -0
- package/dist/hooks/expert-review.js.map +1 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -1
- package/dist/performance/Debouncer.d.ts +91 -0
- package/dist/performance/Debouncer.d.ts.map +1 -0
- package/dist/performance/Debouncer.js +198 -0
- package/dist/performance/Debouncer.js.map +1 -0
- package/dist/performance/MemoryDecay.d.ts +82 -0
- package/dist/performance/MemoryDecay.d.ts.map +1 -0
- package/dist/performance/MemoryDecay.js +153 -0
- package/dist/performance/MemoryDecay.js.map +1 -0
- package/dist/performance/index.d.ts +7 -0
- package/dist/performance/index.d.ts.map +1 -0
- package/dist/performance/index.js +9 -0
- package/dist/performance/index.js.map +1 -0
- package/dist/schema/expert-types.d.ts +395 -0
- package/dist/schema/expert-types.d.ts.map +1 -0
- package/dist/schema/expert-types.js +250 -0
- package/dist/schema/expert-types.js.map +1 -0
- package/dist/schema/plans.d.ts +16 -16
- package/dist/schema/tracking.d.ts +90 -90
- package/dist/services/ContextGenerator.js +7 -7
- package/dist/services/ProjectScaffold.js +76 -76
- package/dist/services/context-injector.d.ts +6 -0
- package/dist/services/context-injector.d.ts.map +1 -1
- package/dist/services/context-injector.js +43 -3
- package/dist/services/context-injector.js.map +1 -1
- package/dist/services/memory-router.d.ts +25 -1
- package/dist/services/memory-router.d.ts.map +1 -1
- package/dist/services/memory-router.js +176 -32
- package/dist/services/memory-router.js.map +1 -1
- package/dist/services/pattern-tracker.d.ts +5 -1
- package/dist/services/pattern-tracker.d.ts.map +1 -1
- package/dist/services/pattern-tracker.js +114 -36
- package/dist/services/pattern-tracker.js.map +1 -1
- package/dist/services/semantic-search.js +2 -2
- package/dist/types/index.d.ts +329 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +186 -9
- package/dist/types/index.js.map +1 -1
- package/package.json +105 -100
- package/standards/README.md +50 -50
- package/standards/experts/expert-routing.md +215 -215
- package/standards/general/code-quality.md +86 -86
- package/standards/general/memory-usage.md +205 -205
- package/standards/general/sync-workflow.md +235 -235
- package/standards/general/workflow.md +82 -82
- package/standards/hooks/mandatory-tracking.md +446 -446
- package/standards/infrastructure/cloud-database.md +287 -287
- package/standards/mcp/server-design.md +243 -243
- package/standards/mcp/tool-patterns.md +354 -354
- package/standards/skills/skill-structure.md +286 -286
- package/standards/skills/workflow-design.md +323 -323
- package/standards/tools/tool-design.md +297 -297
- package/templates/agents/architecture-expert.md +61 -61
- package/templates/agents/database-expert.md +62 -62
- package/templates/agents/documentation-expert.md +57 -57
- package/templates/agents/memory-expert.md +88 -88
- package/templates/agents/performance-expert.md +61 -61
- package/templates/agents/security-expert.md +59 -59
- package/templates/agents/ux-expert.md +63 -63
- package/templates/agents/worker.md +75 -75
- package/templates/ai-skills/SKILL_TEMPLATE.md +55 -55
- package/templates/claude-settings.json +72 -72
- package/templates/commands/experts.md +138 -138
- package/templates/hooks/README.md +158 -158
- package/templates/hooks/project.config.json.template +77 -77
- package/templates/hooks/settings.local.json.template +57 -57
- package/templates/memory-config.json +56 -56
- package/templates/memory-config.schema.json +212 -212
- package/templates/settings.json +58 -58
- package/templates/skills/continue.md +205 -205
- package/templates/workflows/business-improvement.md +264 -264
- package/templates/workflows/expert-review.md +153 -153
- package/templates/workflows/internal-app.md +245 -245
- package/templates/workflows/sync-docs.md +187 -187
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/events/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Event Type Definitions for cmp-standards v2.8
|
|
3
|
+
* @description Type-safe event system for expert panel orchestration
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
// =============================================================================
|
|
7
|
+
// Event Base Schema
|
|
8
|
+
// =============================================================================
|
|
9
|
+
export const EventMetadataSchema = z.object({
|
|
10
|
+
eventId: z.string(),
|
|
11
|
+
timestamp: z.string(),
|
|
12
|
+
source: z.string(),
|
|
13
|
+
correlationId: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Task Events
|
|
17
|
+
// =============================================================================
|
|
18
|
+
export const TaskNeedsReviewEventSchema = z.object({
|
|
19
|
+
type: z.literal('task.needs_review'),
|
|
20
|
+
taskId: z.string(),
|
|
21
|
+
files: z.array(z.string()),
|
|
22
|
+
criticality: z.enum(['critical', 'normal']),
|
|
23
|
+
domain: z.string().optional(),
|
|
24
|
+
metadata: EventMetadataSchema,
|
|
25
|
+
});
|
|
26
|
+
export const TaskReviewedEventSchema = z.object({
|
|
27
|
+
type: z.literal('task.reviewed'),
|
|
28
|
+
taskId: z.string(),
|
|
29
|
+
decision: z.enum(['APPROVED', 'REJECTED', 'NEEDS_REVIEW']),
|
|
30
|
+
metadata: EventMetadataSchema,
|
|
31
|
+
});
|
|
32
|
+
export const TaskCompletedEventSchema = z.object({
|
|
33
|
+
type: z.literal('task.completed'),
|
|
34
|
+
taskId: z.string(),
|
|
35
|
+
result: z.enum(['success', 'partial', 'failed']),
|
|
36
|
+
metadata: EventMetadataSchema,
|
|
37
|
+
});
|
|
38
|
+
// =============================================================================
|
|
39
|
+
// Expert Events
|
|
40
|
+
// =============================================================================
|
|
41
|
+
export const ExpertVoteSubmittedEventSchema = z.object({
|
|
42
|
+
type: z.literal('expert.vote_submitted'),
|
|
43
|
+
expertId: z.string(),
|
|
44
|
+
taskId: z.string(),
|
|
45
|
+
vote: z.enum(['APPROVE', 'REJECT', 'WARN', 'ABSTAIN']),
|
|
46
|
+
confidence: z.number().min(0).max(1),
|
|
47
|
+
issues: z.array(z.object({
|
|
48
|
+
severity: z.enum(['critical', 'high', 'medium', 'low']),
|
|
49
|
+
message: z.string(),
|
|
50
|
+
file: z.string().optional(),
|
|
51
|
+
line: z.number().optional(),
|
|
52
|
+
})),
|
|
53
|
+
reasoning: z.string().optional(),
|
|
54
|
+
metadata: EventMetadataSchema,
|
|
55
|
+
});
|
|
56
|
+
export const ExpertRecommendationEventSchema = z.object({
|
|
57
|
+
type: z.literal('expert.recommendation'),
|
|
58
|
+
expertId: z.string(),
|
|
59
|
+
taskId: z.string(),
|
|
60
|
+
recommendation: z.object({
|
|
61
|
+
title: z.string(),
|
|
62
|
+
description: z.string(),
|
|
63
|
+
priority: z.enum(['critical', 'high', 'medium', 'low']),
|
|
64
|
+
effort: z.enum(['trivial', 'small', 'medium', 'large']),
|
|
65
|
+
}),
|
|
66
|
+
metadata: EventMetadataSchema,
|
|
67
|
+
});
|
|
68
|
+
// =============================================================================
|
|
69
|
+
// Consensus Events
|
|
70
|
+
// =============================================================================
|
|
71
|
+
export const ConsensusReachedEventSchema = z.object({
|
|
72
|
+
type: z.literal('consensus.reached'),
|
|
73
|
+
taskId: z.string(),
|
|
74
|
+
decision: z.enum(['APPROVED', 'REJECTED', 'NEEDS_REVIEW']),
|
|
75
|
+
threshold: z.enum(['UNANIMITY', 'MAJORITY']),
|
|
76
|
+
votes: z.object({
|
|
77
|
+
approve: z.number(),
|
|
78
|
+
reject: z.number(),
|
|
79
|
+
abstain: z.number(),
|
|
80
|
+
}),
|
|
81
|
+
metadata: EventMetadataSchema,
|
|
82
|
+
});
|
|
83
|
+
// =============================================================================
|
|
84
|
+
// Pattern Events
|
|
85
|
+
// =============================================================================
|
|
86
|
+
export const PatternDetectedEventSchema = z.object({
|
|
87
|
+
type: z.literal('pattern.detected'),
|
|
88
|
+
patternId: z.string(),
|
|
89
|
+
occurrences: z.number(),
|
|
90
|
+
files: z.array(z.string()),
|
|
91
|
+
severity: z.enum(['critical', 'high', 'medium', 'low']),
|
|
92
|
+
autoImprove: z.boolean(),
|
|
93
|
+
metadata: EventMetadataSchema,
|
|
94
|
+
});
|
|
95
|
+
// =============================================================================
|
|
96
|
+
// Memory Events
|
|
97
|
+
// =============================================================================
|
|
98
|
+
export const MemoryCreatedEventSchema = z.object({
|
|
99
|
+
type: z.literal('memory.created'),
|
|
100
|
+
memoryId: z.string(),
|
|
101
|
+
domain: z.string(),
|
|
102
|
+
title: z.string(),
|
|
103
|
+
metadata: EventMetadataSchema,
|
|
104
|
+
});
|
|
105
|
+
export const MemoryAccessedEventSchema = z.object({
|
|
106
|
+
type: z.literal('memory.accessed'),
|
|
107
|
+
memoryId: z.string(),
|
|
108
|
+
context: z.string().optional(),
|
|
109
|
+
metadata: EventMetadataSchema,
|
|
110
|
+
});
|
|
111
|
+
// =============================================================================
|
|
112
|
+
// Union Types
|
|
113
|
+
// =============================================================================
|
|
114
|
+
export const EventSchema = z.discriminatedUnion('type', [
|
|
115
|
+
TaskNeedsReviewEventSchema,
|
|
116
|
+
TaskReviewedEventSchema,
|
|
117
|
+
TaskCompletedEventSchema,
|
|
118
|
+
ExpertVoteSubmittedEventSchema,
|
|
119
|
+
ExpertRecommendationEventSchema,
|
|
120
|
+
ConsensusReachedEventSchema,
|
|
121
|
+
PatternDetectedEventSchema,
|
|
122
|
+
MemoryCreatedEventSchema,
|
|
123
|
+
MemoryAccessedEventSchema,
|
|
124
|
+
]);
|
|
125
|
+
// =============================================================================
|
|
126
|
+
// Event Factory Helpers
|
|
127
|
+
// =============================================================================
|
|
128
|
+
export function createEventMetadata(source, correlationId) {
|
|
129
|
+
return {
|
|
130
|
+
eventId: `evt_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
|
|
131
|
+
timestamp: new Date().toISOString(),
|
|
132
|
+
source,
|
|
133
|
+
correlationId,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/events/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ConsensusEngine - Voting logic for expert panel
|
|
3
|
+
* @description Calculates consensus from expert votes with configurable thresholds
|
|
4
|
+
*/
|
|
5
|
+
import { z } from 'zod';
|
|
6
|
+
import { ExpertVote, ConsensusDecisionSchema, ConsensusThresholdSchema } from '../schema/expert-types.js';
|
|
7
|
+
export type ConsensusDecision = z.infer<typeof ConsensusDecisionSchema>;
|
|
8
|
+
export type ConsensusThreshold = z.infer<typeof ConsensusThresholdSchema>;
|
|
9
|
+
export interface VoteRecord {
|
|
10
|
+
expertId: string;
|
|
11
|
+
vote: ExpertVote;
|
|
12
|
+
confidence: number;
|
|
13
|
+
decisionId?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ConsensusInput {
|
|
16
|
+
taskId: string;
|
|
17
|
+
votes: VoteRecord[];
|
|
18
|
+
threshold: ConsensusThreshold;
|
|
19
|
+
startTime: number;
|
|
20
|
+
}
|
|
21
|
+
export interface ConsensusResult {
|
|
22
|
+
decision: ConsensusDecision;
|
|
23
|
+
threshold: ConsensusThreshold;
|
|
24
|
+
metThreshold: boolean;
|
|
25
|
+
votes: {
|
|
26
|
+
approve: number;
|
|
27
|
+
reject: number;
|
|
28
|
+
warn: number;
|
|
29
|
+
abstain: number;
|
|
30
|
+
};
|
|
31
|
+
avgConfidence: number;
|
|
32
|
+
expertDecisionIds: string[];
|
|
33
|
+
panelDurationMs: number;
|
|
34
|
+
}
|
|
35
|
+
export declare class ConsensusEngine {
|
|
36
|
+
/**
|
|
37
|
+
* Calculate consensus from votes
|
|
38
|
+
*/
|
|
39
|
+
calculate(input: ConsensusInput): ConsensusResult;
|
|
40
|
+
/**
|
|
41
|
+
* Determine the final decision based on vote counts and threshold
|
|
42
|
+
*/
|
|
43
|
+
private determineDecision;
|
|
44
|
+
/**
|
|
45
|
+
* Check if a single vote would change the outcome
|
|
46
|
+
*/
|
|
47
|
+
wouldChangeOutcome(currentResult: ConsensusResult, newVote: ExpertVote): boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Get votes needed to change outcome
|
|
50
|
+
*/
|
|
51
|
+
votesNeededToChange(currentResult: ConsensusResult): {
|
|
52
|
+
forApproval: number;
|
|
53
|
+
forRejection: number;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
export declare function getConsensusEngine(): ConsensusEngine;
|
|
57
|
+
//# sourceMappingURL=ConsensusEngine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsensusEngine.d.ts","sourceRoot":"","sources":["../../src/experts/ConsensusEngine.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,EACL,UAAU,EACV,uBAAuB,EACvB,wBAAwB,EAEzB,MAAM,2BAA2B,CAAA;AAMlC,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAA;AACvE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAA;AAEzE,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,UAAU,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,SAAS,EAAE,kBAAkB,CAAA;IAC7B,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,kBAAkB,CAAA;IAC7B,YAAY,EAAE,OAAO,CAAA;IACrB,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAA;QACf,MAAM,EAAE,MAAM,CAAA;QACd,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;IACD,aAAa,EAAE,MAAM,CAAA;IACrB,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,eAAe,EAAE,MAAM,CAAA;CACxB;AAMD,qBAAa,eAAe;IAC1B;;OAEG;IACH,SAAS,CAAC,KAAK,EAAE,cAAc,GAAG,eAAe;IA4DjD;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAqCzB;;OAEG;IACH,kBAAkB,CAChB,aAAa,EAAE,eAAe,EAC9B,OAAO,EAAE,UAAU,GAClB,OAAO;IA+BV;;OAEG;IACH,mBAAmB,CAAC,aAAa,EAAE,eAAe,GAAG;QACnD,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;KACrB;CAoBF;AAQD,wBAAgB,kBAAkB,IAAI,eAAe,CAKpD"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ConsensusEngine - Voting logic for expert panel
|
|
3
|
+
* @description Calculates consensus from expert votes with configurable thresholds
|
|
4
|
+
*/
|
|
5
|
+
// =============================================================================
|
|
6
|
+
// ConsensusEngine Implementation
|
|
7
|
+
// =============================================================================
|
|
8
|
+
export class ConsensusEngine {
|
|
9
|
+
/**
|
|
10
|
+
* Calculate consensus from votes
|
|
11
|
+
*/
|
|
12
|
+
calculate(input) {
|
|
13
|
+
const { taskId, votes, threshold, startTime } = input;
|
|
14
|
+
// Count votes
|
|
15
|
+
const counts = {
|
|
16
|
+
approve: 0,
|
|
17
|
+
reject: 0,
|
|
18
|
+
warn: 0,
|
|
19
|
+
abstain: 0,
|
|
20
|
+
};
|
|
21
|
+
let totalConfidence = 0;
|
|
22
|
+
const decisionIds = [];
|
|
23
|
+
for (const vote of votes) {
|
|
24
|
+
switch (vote.vote) {
|
|
25
|
+
case 'APPROVE':
|
|
26
|
+
counts.approve++;
|
|
27
|
+
break;
|
|
28
|
+
case 'REJECT':
|
|
29
|
+
counts.reject++;
|
|
30
|
+
break;
|
|
31
|
+
case 'WARN':
|
|
32
|
+
counts.warn++;
|
|
33
|
+
break;
|
|
34
|
+
case 'ABSTAIN':
|
|
35
|
+
counts.abstain++;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
totalConfidence += vote.confidence;
|
|
39
|
+
if (vote.decisionId) {
|
|
40
|
+
decisionIds.push(vote.decisionId);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Calculate average confidence (excluding abstains)
|
|
44
|
+
const activeVotes = votes.filter(v => v.vote !== 'ABSTAIN');
|
|
45
|
+
const avgConfidence = activeVotes.length > 0
|
|
46
|
+
? totalConfidence / activeVotes.length
|
|
47
|
+
: 0;
|
|
48
|
+
// Determine decision based on threshold
|
|
49
|
+
const { decision, metThreshold } = this.determineDecision(counts, threshold, activeVotes.length);
|
|
50
|
+
return {
|
|
51
|
+
decision,
|
|
52
|
+
threshold,
|
|
53
|
+
metThreshold,
|
|
54
|
+
votes: counts,
|
|
55
|
+
avgConfidence,
|
|
56
|
+
expertDecisionIds: decisionIds,
|
|
57
|
+
panelDurationMs: Date.now() - startTime,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Determine the final decision based on vote counts and threshold
|
|
62
|
+
*/
|
|
63
|
+
determineDecision(counts, threshold, activeVotes) {
|
|
64
|
+
// No active votes = needs review
|
|
65
|
+
if (activeVotes === 0) {
|
|
66
|
+
return { decision: 'NEEDS_REVIEW', metThreshold: false };
|
|
67
|
+
}
|
|
68
|
+
// UNANIMITY: All must approve (no rejects)
|
|
69
|
+
if (threshold === 'UNANIMITY') {
|
|
70
|
+
if (counts.reject > 0) {
|
|
71
|
+
return { decision: 'REJECTED', metThreshold: false };
|
|
72
|
+
}
|
|
73
|
+
if (counts.warn > 0 && counts.approve === 0) {
|
|
74
|
+
return { decision: 'NEEDS_REVIEW', metThreshold: false };
|
|
75
|
+
}
|
|
76
|
+
// All approved or warned (no rejects)
|
|
77
|
+
return { decision: 'APPROVED', metThreshold: true };
|
|
78
|
+
}
|
|
79
|
+
// MAJORITY: More than half must approve
|
|
80
|
+
const majorityThreshold = Math.ceil(activeVotes / 2);
|
|
81
|
+
if (counts.approve >= majorityThreshold) {
|
|
82
|
+
return { decision: 'APPROVED', metThreshold: true };
|
|
83
|
+
}
|
|
84
|
+
if (counts.reject >= majorityThreshold) {
|
|
85
|
+
return { decision: 'REJECTED', metThreshold: true };
|
|
86
|
+
}
|
|
87
|
+
// No clear majority
|
|
88
|
+
return { decision: 'NEEDS_REVIEW', metThreshold: false };
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Check if a single vote would change the outcome
|
|
92
|
+
*/
|
|
93
|
+
wouldChangeOutcome(currentResult, newVote) {
|
|
94
|
+
// Clone counts
|
|
95
|
+
const newCounts = { ...currentResult.votes };
|
|
96
|
+
// Add new vote
|
|
97
|
+
switch (newVote) {
|
|
98
|
+
case 'APPROVE':
|
|
99
|
+
newCounts.approve++;
|
|
100
|
+
break;
|
|
101
|
+
case 'REJECT':
|
|
102
|
+
newCounts.reject++;
|
|
103
|
+
break;
|
|
104
|
+
case 'WARN':
|
|
105
|
+
newCounts.warn++;
|
|
106
|
+
break;
|
|
107
|
+
case 'ABSTAIN':
|
|
108
|
+
newCounts.abstain++;
|
|
109
|
+
return false; // Abstain never changes outcome
|
|
110
|
+
}
|
|
111
|
+
const activeVotes = newCounts.approve + newCounts.reject + newCounts.warn;
|
|
112
|
+
const { decision } = this.determineDecision(newCounts, currentResult.threshold, activeVotes);
|
|
113
|
+
return decision !== currentResult.decision;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Get votes needed to change outcome
|
|
117
|
+
*/
|
|
118
|
+
votesNeededToChange(currentResult) {
|
|
119
|
+
const activeVotes = currentResult.votes.approve +
|
|
120
|
+
currentResult.votes.reject +
|
|
121
|
+
currentResult.votes.warn;
|
|
122
|
+
if (currentResult.threshold === 'UNANIMITY') {
|
|
123
|
+
return {
|
|
124
|
+
forApproval: currentResult.votes.reject > 0 ? Infinity : 0,
|
|
125
|
+
forRejection: 1, // Any reject blocks
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
// MAJORITY
|
|
129
|
+
const majorityThreshold = Math.ceil((activeVotes + 1) / 2);
|
|
130
|
+
return {
|
|
131
|
+
forApproval: Math.max(0, majorityThreshold - currentResult.votes.approve),
|
|
132
|
+
forRejection: Math.max(0, majorityThreshold - currentResult.votes.reject),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// =============================================================================
|
|
137
|
+
// Singleton
|
|
138
|
+
// =============================================================================
|
|
139
|
+
let globalEngine = null;
|
|
140
|
+
export function getConsensusEngine() {
|
|
141
|
+
if (!globalEngine) {
|
|
142
|
+
globalEngine = new ConsensusEngine();
|
|
143
|
+
}
|
|
144
|
+
return globalEngine;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=ConsensusEngine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConsensusEngine.js","sourceRoot":"","sources":["../../src/experts/ConsensusEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ExpertPanelService - Full expert panel orchestration
|
|
3
|
+
* @description Coordinates expert routing, vote collection, and consensus
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Receive task for review
|
|
7
|
+
* 2. Detect criticality and route to experts
|
|
8
|
+
* 3. Collect votes in parallel
|
|
9
|
+
* 4. Calculate consensus
|
|
10
|
+
* 5. Emit events and return result
|
|
11
|
+
*/
|
|
12
|
+
import { RoutingContext, RoutingResult, ExpertId, ExpertSelection } from './ExpertRouter.js';
|
|
13
|
+
import { ConsensusResult, ConsensusThreshold } from './ConsensusEngine.js';
|
|
14
|
+
import { ExpertVoteResult, ExpertVoteProvider } from './VoteCollector.js';
|
|
15
|
+
import { ExpertIssue } from '../schema/expert-types.js';
|
|
16
|
+
export interface PanelRequest {
|
|
17
|
+
taskId: string;
|
|
18
|
+
files: string[];
|
|
19
|
+
content?: string;
|
|
20
|
+
diff?: string;
|
|
21
|
+
domain?: string;
|
|
22
|
+
/** Force specific threshold (overrides auto-detection) */
|
|
23
|
+
forceThreshold?: ConsensusThreshold;
|
|
24
|
+
/** Include only specific experts */
|
|
25
|
+
includeExperts?: ExpertId[];
|
|
26
|
+
/** Exclude specific experts */
|
|
27
|
+
excludeExperts?: ExpertId[];
|
|
28
|
+
}
|
|
29
|
+
export interface PanelResult {
|
|
30
|
+
taskId: string;
|
|
31
|
+
panelId: string;
|
|
32
|
+
routing: RoutingResult;
|
|
33
|
+
votes: ExpertVoteResult[];
|
|
34
|
+
consensus: ConsensusResult;
|
|
35
|
+
allIssues: ExpertIssue[];
|
|
36
|
+
timing: PanelTiming;
|
|
37
|
+
}
|
|
38
|
+
export interface PanelTiming {
|
|
39
|
+
startedAt: string;
|
|
40
|
+
completedAt: string;
|
|
41
|
+
routingMs: number;
|
|
42
|
+
votingMs: number;
|
|
43
|
+
consensusMs: number;
|
|
44
|
+
totalMs: number;
|
|
45
|
+
}
|
|
46
|
+
export interface ExpertPanelConfig {
|
|
47
|
+
/** Timeout per expert vote */
|
|
48
|
+
voteTimeout: number;
|
|
49
|
+
/** Enable debug logging */
|
|
50
|
+
debug: boolean;
|
|
51
|
+
}
|
|
52
|
+
export declare class ExpertPanelService {
|
|
53
|
+
private config;
|
|
54
|
+
private router;
|
|
55
|
+
private collector;
|
|
56
|
+
private consensusEngine;
|
|
57
|
+
private eventBus;
|
|
58
|
+
private voteProvider;
|
|
59
|
+
constructor(config?: Partial<ExpertPanelConfig>);
|
|
60
|
+
/**
|
|
61
|
+
* Set the vote provider (how to get votes from experts)
|
|
62
|
+
* This allows injection of different providers (real API, mock, etc.)
|
|
63
|
+
*/
|
|
64
|
+
setVoteProvider(provider: ExpertVoteProvider): void;
|
|
65
|
+
/**
|
|
66
|
+
* Run the full expert panel for a task
|
|
67
|
+
*/
|
|
68
|
+
runPanel(request: PanelRequest): Promise<PanelResult>;
|
|
69
|
+
/**
|
|
70
|
+
* Apply include/exclude filters to expert selection
|
|
71
|
+
*/
|
|
72
|
+
private applyExpertFilters;
|
|
73
|
+
/**
|
|
74
|
+
* Quick check if a task needs review
|
|
75
|
+
*/
|
|
76
|
+
needsReview(files: string[], content?: string): boolean;
|
|
77
|
+
/**
|
|
78
|
+
* Get which experts would review a task (without running the panel)
|
|
79
|
+
*/
|
|
80
|
+
previewExperts(context: RoutingContext): ExpertSelection[];
|
|
81
|
+
}
|
|
82
|
+
export declare function getExpertPanelService(config?: Partial<ExpertPanelConfig>): ExpertPanelService;
|
|
83
|
+
export declare function resetExpertPanelService(): void;
|
|
84
|
+
//# sourceMappingURL=ExpertPanelService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ExpertPanelService.d.ts","sourceRoot":"","sources":["../../src/experts/ExpertPanelService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,EAGL,cAAc,EACd,aAAa,EACb,QAAQ,EACR,eAAe,EAEhB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAGL,eAAe,EACf,kBAAkB,EACnB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAIL,gBAAgB,EAChB,kBAAkB,EAEnB,MAAM,oBAAoB,CAAA;AAC3B,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAA;AAOvD,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,cAAc,CAAC,EAAE,kBAAkB,CAAA;IACnC,oCAAoC;IACpC,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAA;IAC3B,+BAA+B;IAC/B,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAA;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,aAAa,CAAA;IACtB,KAAK,EAAE,gBAAgB,EAAE,CAAA;IACzB,SAAS,EAAE,eAAe,CAAA;IAC1B,SAAS,EAAE,WAAW,EAAE,CAAA;IACxB,MAAM,EAAE,WAAW,CAAA;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,2BAA2B;IAC3B,KAAK,EAAE,OAAO,CAAA;CACf;AAWD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAAmB;IACjC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAe;IAChC,OAAO,CAAC,eAAe,CAAiB;IACxC,OAAO,CAAC,QAAQ,CAAU;IAC1B,OAAO,CAAC,YAAY,CAAkC;gBAE1C,MAAM,GAAE,OAAO,CAAC,iBAAiB,CAAM;IAQnD;;;OAGG;IACH,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,IAAI;IAInD;;OAEG;IACG,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC;IAwI3D;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAsB1B;;OAEG;IACH,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO;IAKvD;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,cAAc,GAAG,eAAe,EAAE;CAG3D;AAQD,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,kBAAkB,CAK7F;AAED,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C"}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file ExpertPanelService - Full expert panel orchestration
|
|
3
|
+
* @description Coordinates expert routing, vote collection, and consensus
|
|
4
|
+
*
|
|
5
|
+
* Flow:
|
|
6
|
+
* 1. Receive task for review
|
|
7
|
+
* 2. Detect criticality and route to experts
|
|
8
|
+
* 3. Collect votes in parallel
|
|
9
|
+
* 4. Calculate consensus
|
|
10
|
+
* 5. Emit events and return result
|
|
11
|
+
*/
|
|
12
|
+
import { getEventBus } from '../events/EventBus.js';
|
|
13
|
+
import { createEventMetadata } from '../events/types.js';
|
|
14
|
+
import { getExpertRouter, } from './ExpertRouter.js';
|
|
15
|
+
import { getConsensusEngine, } from './ConsensusEngine.js';
|
|
16
|
+
import { getVoteCollector, } from './VoteCollector.js';
|
|
17
|
+
import { ulid } from 'ulid';
|
|
18
|
+
const DEFAULT_CONFIG = {
|
|
19
|
+
voteTimeout: 5000,
|
|
20
|
+
debug: false,
|
|
21
|
+
};
|
|
22
|
+
// =============================================================================
|
|
23
|
+
// ExpertPanelService Implementation
|
|
24
|
+
// =============================================================================
|
|
25
|
+
export class ExpertPanelService {
|
|
26
|
+
config;
|
|
27
|
+
router;
|
|
28
|
+
collector;
|
|
29
|
+
consensusEngine;
|
|
30
|
+
eventBus;
|
|
31
|
+
voteProvider = null;
|
|
32
|
+
constructor(config = {}) {
|
|
33
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
34
|
+
this.router = getExpertRouter();
|
|
35
|
+
this.collector = getVoteCollector({ voteTimeout: this.config.voteTimeout });
|
|
36
|
+
this.consensusEngine = getConsensusEngine();
|
|
37
|
+
this.eventBus = getEventBus();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Set the vote provider (how to get votes from experts)
|
|
41
|
+
* This allows injection of different providers (real API, mock, etc.)
|
|
42
|
+
*/
|
|
43
|
+
setVoteProvider(provider) {
|
|
44
|
+
this.voteProvider = provider;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Run the full expert panel for a task
|
|
48
|
+
*/
|
|
49
|
+
async runPanel(request) {
|
|
50
|
+
if (!this.voteProvider) {
|
|
51
|
+
throw new Error('Vote provider not set. Call setVoteProvider() first.');
|
|
52
|
+
}
|
|
53
|
+
const panelId = `panel-${ulid()}`;
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
const startedAt = new Date().toISOString();
|
|
56
|
+
if (this.config.debug) {
|
|
57
|
+
console.log(`[ExpertPanel] Starting panel ${panelId} for task ${request.taskId}`);
|
|
58
|
+
}
|
|
59
|
+
// Step 1: Route to experts (do this first to get criticality)
|
|
60
|
+
const routingStart = Date.now();
|
|
61
|
+
const routingContext = {
|
|
62
|
+
files: request.files,
|
|
63
|
+
content: request.content,
|
|
64
|
+
domain: request.domain,
|
|
65
|
+
};
|
|
66
|
+
let routing = this.router.route(routingContext);
|
|
67
|
+
const routingMs = Date.now() - routingStart;
|
|
68
|
+
// Emit task needs review event (now we have criticality)
|
|
69
|
+
await this.eventBus.publish({
|
|
70
|
+
type: 'task.needs_review',
|
|
71
|
+
taskId: request.taskId,
|
|
72
|
+
files: request.files,
|
|
73
|
+
criticality: routing.criticality.level,
|
|
74
|
+
domain: request.domain,
|
|
75
|
+
metadata: createEventMetadata('ExpertPanelService'),
|
|
76
|
+
});
|
|
77
|
+
// Apply expert filters
|
|
78
|
+
routing = this.applyExpertFilters(routing, request);
|
|
79
|
+
// Override threshold if forced
|
|
80
|
+
if (request.forceThreshold) {
|
|
81
|
+
routing = { ...routing, threshold: request.forceThreshold };
|
|
82
|
+
}
|
|
83
|
+
if (this.config.debug) {
|
|
84
|
+
console.log(`[ExpertPanel] Routing complete in ${routingMs}ms`);
|
|
85
|
+
console.log(`[ExpertPanel] Criticality: ${routing.criticality.level}`);
|
|
86
|
+
console.log(`[ExpertPanel] Experts: ${routing.selectedExperts.map(e => e.expertId).join(', ')}`);
|
|
87
|
+
console.log(`[ExpertPanel] Threshold: ${routing.threshold}`);
|
|
88
|
+
}
|
|
89
|
+
// Step 2: Collect votes in parallel
|
|
90
|
+
const votingStart = Date.now();
|
|
91
|
+
const reviewContext = {
|
|
92
|
+
files: request.files,
|
|
93
|
+
content: request.content,
|
|
94
|
+
diff: request.diff,
|
|
95
|
+
domain: request.domain,
|
|
96
|
+
};
|
|
97
|
+
const voteCollection = await this.collector.collectVotes(request.taskId, routing.selectedExperts, reviewContext, this.voteProvider);
|
|
98
|
+
const votingMs = Date.now() - votingStart;
|
|
99
|
+
if (this.config.debug) {
|
|
100
|
+
console.log(`[ExpertPanel] Vote collection complete in ${votingMs}ms`);
|
|
101
|
+
console.log(`[ExpertPanel] Votes: ${voteCollection.completed}/${routing.selectedExperts.length}`);
|
|
102
|
+
}
|
|
103
|
+
// Step 3: Calculate consensus
|
|
104
|
+
const consensusStart = Date.now();
|
|
105
|
+
const voteRecords = this.collector.toVoteRecords(voteCollection.votes);
|
|
106
|
+
const consensus = this.consensusEngine.calculate({
|
|
107
|
+
taskId: request.taskId,
|
|
108
|
+
votes: voteRecords,
|
|
109
|
+
threshold: routing.threshold,
|
|
110
|
+
startTime,
|
|
111
|
+
});
|
|
112
|
+
const consensusMs = Date.now() - consensusStart;
|
|
113
|
+
// Collect all issues
|
|
114
|
+
const allIssues = voteCollection.votes.flatMap(v => v.issues);
|
|
115
|
+
// Build timing info
|
|
116
|
+
const completedAt = new Date().toISOString();
|
|
117
|
+
const totalMs = Date.now() - startTime;
|
|
118
|
+
const timing = {
|
|
119
|
+
startedAt,
|
|
120
|
+
completedAt,
|
|
121
|
+
routingMs,
|
|
122
|
+
votingMs,
|
|
123
|
+
consensusMs,
|
|
124
|
+
totalMs,
|
|
125
|
+
};
|
|
126
|
+
if (this.config.debug) {
|
|
127
|
+
console.log(`[ExpertPanel] Panel complete in ${totalMs}ms`);
|
|
128
|
+
console.log(`[ExpertPanel] Decision: ${consensus.decision}`);
|
|
129
|
+
console.log(`[ExpertPanel] Issues found: ${allIssues.length}`);
|
|
130
|
+
}
|
|
131
|
+
// Emit consensus reached event
|
|
132
|
+
await this.eventBus.publish({
|
|
133
|
+
type: 'consensus.reached',
|
|
134
|
+
taskId: request.taskId,
|
|
135
|
+
decision: consensus.decision,
|
|
136
|
+
threshold: consensus.threshold,
|
|
137
|
+
metThreshold: consensus.metThreshold,
|
|
138
|
+
votes: consensus.votes,
|
|
139
|
+
expertDecisionIds: consensus.expertDecisionIds,
|
|
140
|
+
metadata: createEventMetadata('ExpertPanelService'),
|
|
141
|
+
});
|
|
142
|
+
// Emit task reviewed event
|
|
143
|
+
await this.eventBus.publish({
|
|
144
|
+
type: 'task.reviewed',
|
|
145
|
+
taskId: request.taskId,
|
|
146
|
+
decision: consensus.decision,
|
|
147
|
+
metadata: createEventMetadata('ExpertPanelService'),
|
|
148
|
+
});
|
|
149
|
+
return {
|
|
150
|
+
taskId: request.taskId,
|
|
151
|
+
panelId,
|
|
152
|
+
routing,
|
|
153
|
+
votes: voteCollection.votes,
|
|
154
|
+
consensus,
|
|
155
|
+
allIssues,
|
|
156
|
+
timing,
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Apply include/exclude filters to expert selection
|
|
161
|
+
*/
|
|
162
|
+
applyExpertFilters(routing, request) {
|
|
163
|
+
let experts = routing.selectedExperts;
|
|
164
|
+
// Include filter
|
|
165
|
+
if (request.includeExperts && request.includeExperts.length > 0) {
|
|
166
|
+
experts = experts.filter(e => request.includeExperts.includes(e.expertId));
|
|
167
|
+
}
|
|
168
|
+
// Exclude filter
|
|
169
|
+
if (request.excludeExperts && request.excludeExperts.length > 0) {
|
|
170
|
+
experts = experts.filter(e => !request.excludeExperts.includes(e.expertId));
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
...routing,
|
|
174
|
+
selectedExperts: experts,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Quick check if a task needs review
|
|
179
|
+
*/
|
|
180
|
+
needsReview(files, content) {
|
|
181
|
+
const routing = this.router.route({ files, content });
|
|
182
|
+
return routing.criticality.level === 'critical' || routing.selectedExperts.length >= 2;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get which experts would review a task (without running the panel)
|
|
186
|
+
*/
|
|
187
|
+
previewExperts(context) {
|
|
188
|
+
return this.router.route(context).selectedExperts;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// =============================================================================
|
|
192
|
+
// Singleton
|
|
193
|
+
// =============================================================================
|
|
194
|
+
let globalPanel = null;
|
|
195
|
+
export function getExpertPanelService(config) {
|
|
196
|
+
if (!globalPanel) {
|
|
197
|
+
globalPanel = new ExpertPanelService(config);
|
|
198
|
+
}
|
|
199
|
+
return globalPanel;
|
|
200
|
+
}
|
|
201
|
+
export function resetExpertPanelService() {
|
|
202
|
+
globalPanel = null;
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=ExpertPanelService.js.map
|