@equilateral_ai/mindmeld 3.5.3 → 4.0.2

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.
Files changed (138) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +21 -13
  3. package/scripts/init-project.js +9 -23
  4. package/scripts/repo-analyzer.js +118 -2
  5. package/src/client/dbShim.js +16 -0
  6. package/src/core/AuthManager.js +3 -2
  7. package/src/handlers/helpers/dbOperations.js +9 -46
  8. package/src/index.js +2 -217
  9. package/src/utils/piiMask.js +16 -0
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/standards.js +0 -285
  13. package/src/collaboration/CollaborationPrompt.js +0 -460
  14. package/src/core/AlertEngine.js +0 -813
  15. package/src/core/AlertNotifier.js +0 -363
  16. package/src/core/CorrelationAnalyzer.js +0 -931
  17. package/src/core/CrossReferenceEngine.js +0 -624
  18. package/src/core/CurationEngine.js +0 -688
  19. package/src/core/DeprecationScheduler.js +0 -183
  20. package/src/core/LoadBearingDetector.js +0 -242
  21. package/src/core/NotificationService.js +0 -1032
  22. package/src/core/RapportOrchestrator.js +0 -632
  23. package/src/core/RelevanceDetector.js +0 -694
  24. package/src/core/StandardLifecycle.js +0 -244
  25. package/src/core/StandardsIngestion.js +0 -991
  26. package/src/core/TeamLoadBearingDetector.js +0 -431
  27. package/src/core/parsers/adrParser.js +0 -479
  28. package/src/core/parsers/cursorRulesParser.js +0 -564
  29. package/src/core/parsers/eslintParser.js +0 -439
  30. package/src/database/dbOperations.js +0 -105
  31. package/src/handlers/activity/activityGetMe.js +0 -98
  32. package/src/handlers/activity/activityGetTeam.js +0 -175
  33. package/src/handlers/admin/adminSetup.js +0 -216
  34. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  35. package/src/handlers/alerts/alertsGet.js +0 -250
  36. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  37. package/src/handlers/analytics/coachingGet.js +0 -361
  38. package/src/handlers/analytics/convergenceGet.js +0 -236
  39. package/src/handlers/analytics/developerScoreGet.js +0 -137
  40. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  41. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  42. package/src/handlers/collaborators/collaboratorList.js +0 -82
  43. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  44. package/src/handlers/collaborators/inviteAccept.js +0 -122
  45. package/src/handlers/company/companyUsersDelete.js +0 -141
  46. package/src/handlers/company/companyUsersGet.js +0 -90
  47. package/src/handlers/company/companyUsersPost.js +0 -267
  48. package/src/handlers/company/companyUsersPut.js +0 -76
  49. package/src/handlers/context/contextGet.js +0 -57
  50. package/src/handlers/context/invariantsGet.js +0 -74
  51. package/src/handlers/context/loopsGet.js +0 -82
  52. package/src/handlers/context/notesCreate.js +0 -74
  53. package/src/handlers/context/purposeGet.js +0 -78
  54. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  55. package/src/handlers/correlations/correlationsGet.js +0 -93
  56. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  57. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  58. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  59. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  60. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  61. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  62. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  63. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  64. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  65. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  66. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  67. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  68. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  69. package/src/handlers/github/githubConnectionStatus.js +0 -49
  70. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  71. package/src/handlers/github/githubOAuthCallback.js +0 -178
  72. package/src/handlers/github/githubOAuthStart.js +0 -59
  73. package/src/handlers/github/githubPatternsReview.js +0 -76
  74. package/src/handlers/github/githubReposList.js +0 -105
  75. package/src/handlers/health/healthGet.js +0 -55
  76. package/src/handlers/helpers/auditLogger.js +0 -201
  77. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  78. package/src/handlers/helpers/decisionFrames.js +0 -29
  79. package/src/handlers/helpers/errorHandler.js +0 -49
  80. package/src/handlers/helpers/index.js +0 -138
  81. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  82. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  83. package/src/handlers/helpers/predictiveCache.js +0 -51
  84. package/src/handlers/helpers/projectAccess.js +0 -88
  85. package/src/handlers/helpers/responseUtil.js +0 -55
  86. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  87. package/src/handlers/mcp/mcpHandler.js +0 -569
  88. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  89. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  90. package/src/handlers/notifications/getPreferences.js +0 -84
  91. package/src/handlers/notifications/sendNotification.js +0 -170
  92. package/src/handlers/notifications/updatePreferences.js +0 -316
  93. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  94. package/src/handlers/patterns/patternUsagePost.js +0 -182
  95. package/src/handlers/patterns/patternViolationPost.js +0 -185
  96. package/src/handlers/projects/projectCreate.js +0 -248
  97. package/src/handlers/projects/projectDelete.js +0 -82
  98. package/src/handlers/projects/projectGet.js +0 -95
  99. package/src/handlers/projects/projectUpdate.js +0 -117
  100. package/src/handlers/reports/aiLeverage.js +0 -210
  101. package/src/handlers/reports/engineeringInvestment.js +0 -132
  102. package/src/handlers/reports/riskForecast.js +0 -206
  103. package/src/handlers/reports/standardsRoi.js +0 -254
  104. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  105. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  106. package/src/handlers/scheduled/generateAlerts.js +0 -135
  107. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  108. package/src/handlers/scheduled/refreshActivity.js +0 -21
  109. package/src/handlers/scheduled/scanCompliance.js +0 -334
  110. package/src/handlers/sessions/sessionEndPost.js +0 -180
  111. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  112. package/src/handlers/standards/catalogGet.js +0 -185
  113. package/src/handlers/standards/catalogSync.js +0 -120
  114. package/src/handlers/standards/discoveriesGet.js +0 -89
  115. package/src/handlers/standards/projectStandardsGet.js +0 -129
  116. package/src/handlers/standards/projectStandardsPut.js +0 -151
  117. package/src/handlers/standards/standardsAuditGet.js +0 -65
  118. package/src/handlers/standards/standardsParseUpload.js +0 -149
  119. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  120. package/src/handlers/standards/standardsTransition.js +0 -161
  121. package/src/handlers/stripe/addonManagePost.js +0 -240
  122. package/src/handlers/stripe/billingPortalPost.js +0 -93
  123. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  124. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  125. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  126. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  127. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  128. package/src/handlers/stripe/webhookPost.js +0 -482
  129. package/src/handlers/user/apiTokenCreate.js +0 -71
  130. package/src/handlers/user/apiTokenList.js +0 -64
  131. package/src/handlers/user/userSplashAck.js +0 -91
  132. package/src/handlers/user/userSplashGet.js +0 -211
  133. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  134. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  135. package/src/handlers/users/userEntitlementsGet.js +0 -89
  136. package/src/handlers/users/userGet.js +0 -118
  137. package/src/handlers/users/userProfilePut.js +0 -77
  138. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,688 +0,0 @@
1
- /**
2
- * Rapport v3 - Curation Engine
3
- *
4
- * Purpose: Promotes validated patterns to .equilateral-standards/
5
- *
6
- * Promotion Criteria:
7
- * - correlation >= 0.90 (90% success rate)
8
- * - projectCount >= 5 (proven across 5+ projects)
9
- * - developerCount >= 3 (used by 3+ developers)
10
- * - sessionCount >= 10 (reinforced 10+ times)
11
- * - hasMeasurableBenefit = true (cost/performance gain)
12
- *
13
- * Based on: /Users/jamesford/Source/rapport/docs/RAPPORT_STANDARDS_INTEGRATION_DESIGN.md
14
- */
15
-
16
- const { executeQuery } = require('../handlers/helpers/dbOperations');
17
- const { NotificationService } = require('./NotificationService');
18
-
19
- class CurationEngine {
20
- constructor(config = {}) {
21
- this.config = {
22
- minCorrelation: config.minCorrelation || 0.90,
23
- minProjects: config.minProjects || 5,
24
- minDevelopers: config.minDevelopers || 3,
25
- minSessions: config.minSessions || 10,
26
- requireMeasurableBenefit: config.requireMeasurableBenefit !== false,
27
- ...config
28
- };
29
-
30
- // Initialize notification service
31
- this.notificationService = config.notificationService || new NotificationService();
32
- }
33
-
34
- /**
35
- * Evaluate if a pattern meets promotion criteria
36
- *
37
- * @param {string} patternId - Pattern identifier
38
- * @returns {Promise<Object|null>} Curation candidate or null
39
- */
40
- async evaluateForPromotion(patternId) {
41
- try {
42
- // Get pattern metrics from materialized view
43
- const metrics = await this.getPatternMetrics(patternId);
44
-
45
- if (!metrics) {
46
- console.log(`[CurationEngine] Pattern ${patternId} not found in metrics`);
47
- return null;
48
- }
49
-
50
- // Check if pattern meets all criteria
51
- if (!this.meetsAllCriteria(metrics)) {
52
- console.log(`[CurationEngine] Pattern ${patternId} does not meet promotion criteria`);
53
- return null;
54
- }
55
-
56
- // Create curation candidate
57
- const candidate = await this.createCurationCandidate(patternId, metrics);
58
-
59
- console.log(`[CurationEngine] Pattern ${patternId} promoted to curation candidate`);
60
- return candidate;
61
- } catch (error) {
62
- console.error('[CurationEngine] Error evaluating pattern for promotion:', error);
63
- throw error;
64
- }
65
- }
66
-
67
- /**
68
- * Get pattern metrics from materialized view
69
- *
70
- * @param {string} patternId - Pattern identifier
71
- * @returns {Promise<Object|null>} Pattern metrics
72
- */
73
- async getPatternMetrics(patternId) {
74
- const query = `
75
- SELECT
76
- pattern_id,
77
- element,
78
- project_count,
79
- developer_count,
80
- session_count,
81
- success_correlation,
82
- last_used
83
- FROM rapport.pattern_metrics
84
- WHERE pattern_id = $1
85
- `;
86
-
87
- const result = await executeQuery(query, [patternId]);
88
- return result.rows.length > 0 ? result.rows[0] : null;
89
- }
90
-
91
- /**
92
- * Check if metrics meet all promotion criteria
93
- *
94
- * @param {Object} metrics - Pattern metrics
95
- * @returns {boolean} True if all criteria met
96
- */
97
- meetsAllCriteria(metrics) {
98
- // PostgreSQL returns numeric values as strings - parse them
99
- const correlation = parseFloat(metrics.success_correlation) || 0;
100
- const projectCount = parseInt(metrics.project_count) || 0;
101
- const developerCount = parseInt(metrics.developer_count) || 0;
102
- const sessionCount = parseInt(metrics.session_count) || 0;
103
-
104
- const checks = {
105
- correlation: correlation >= this.config.minCorrelation,
106
- projects: projectCount >= this.config.minProjects,
107
- developers: developerCount >= this.config.minDevelopers,
108
- sessions: sessionCount >= this.config.minSessions
109
- };
110
-
111
- const allMet = Object.values(checks).every(check => check === true);
112
-
113
- if (!allMet) {
114
- console.log('[CurationEngine] Criteria check:', {
115
- pattern: metrics.pattern_id,
116
- correlation: `${correlation.toFixed(2)} (required: ${this.config.minCorrelation})`,
117
- projects: `${projectCount} (required: ${this.config.minProjects})`,
118
- developers: `${developerCount} (required: ${this.config.minDevelopers})`,
119
- sessions: `${sessionCount} (required: ${this.config.minSessions})`,
120
- passed: checks
121
- });
122
- }
123
-
124
- return allMet;
125
- }
126
-
127
- /**
128
- * Create a curation candidate for human review
129
- *
130
- * @param {string} patternId - Pattern identifier
131
- * @param {Object} metrics - Pattern metrics
132
- * @returns {Promise<Object>} Curation candidate
133
- */
134
- async createCurationCandidate(patternId, metrics) {
135
- // Get pattern details
136
- const patternDetails = await this.getPatternDetails(patternId);
137
-
138
- // Infer category based on pattern characteristics
139
- const category = await this.inferCategory(patternDetails);
140
-
141
- // Gather best examples from successful usage
142
- const examples = await this.gatherBestExamples(patternId);
143
-
144
- // Gather anti-patterns from failed usage
145
- const antiPatterns = await this.gatherAntiPatterns(patternId);
146
-
147
- // Get measurable benefit data
148
- const benefit = await this.getMeasurableBenefit(patternId);
149
-
150
- // Build proposed standard
151
- const proposedStandard = {
152
- name: metrics.element,
153
- category: category,
154
- status: 'candidate',
155
- evidence: {
156
- correlation: parseFloat(metrics.success_correlation),
157
- projectCount: metrics.project_count,
158
- developerCount: metrics.developer_count,
159
- sessionCount: metrics.session_count,
160
- benefit: benefit
161
- },
162
- pattern: patternDetails,
163
- examples: examples,
164
- antiPatterns: antiPatterns,
165
- proposedEnforcement: this.generateEnforcementRule(patternDetails)
166
- };
167
-
168
- // Generate standard document
169
- const standardDocument = this.generateStandardDocument(proposedStandard);
170
-
171
- // Save to curation database
172
- const candidateId = await this.saveCurationCandidate(
173
- patternId,
174
- category,
175
- proposedStandard.evidence,
176
- standardDocument
177
- );
178
-
179
- // Notify for human review
180
- await this.notifyForReview(candidateId, proposedStandard);
181
-
182
- return {
183
- candidateId,
184
- patternId,
185
- ...proposedStandard
186
- };
187
- }
188
-
189
- /**
190
- * Get detailed pattern information
191
- *
192
- * @param {string} patternId - Pattern identifier
193
- * @returns {Promise<Object>} Pattern details
194
- */
195
- async getPatternDetails(patternId) {
196
- const query = `
197
- SELECT
198
- pattern_id,
199
- project_id,
200
- intent,
201
- constraints,
202
- outcome_criteria,
203
- maturity,
204
- handoff_count,
205
- successful_handoffs,
206
- failed_handoffs,
207
- discovered_by,
208
- discovered_at,
209
- last_used,
210
- pattern_data
211
- FROM rapport.patterns
212
- WHERE pattern_id = $1
213
- `;
214
-
215
- const result = await executeQuery(query, [patternId]);
216
- return result.rows[0];
217
- }
218
-
219
- /**
220
- * Infer category for the pattern based on content analysis
221
- *
222
- * @param {Object} pattern - Pattern details
223
- * @returns {Promise<string>} Inferred category
224
- */
225
- async inferCategory(pattern) {
226
- const intent = pattern.intent.toLowerCase();
227
- const constraints = JSON.stringify(pattern.constraints).toLowerCase();
228
- const data = JSON.stringify(pattern.pattern_data).toLowerCase();
229
-
230
- // Category detection based on keywords
231
- const categories = {
232
- 'serverless-saas-aws': ['lambda', 'api gateway', 'sam', 'serverless', 'handler', 'aws'],
233
- 'database-patterns': ['database', 'query', 'postgresql', 'sql', 'schema'],
234
- 'multi-agent-orchestration': ['agent', 'orchestration', 'workflow', 'task', 'team'],
235
- 'frontend-development': ['react', 'component', 'ui', 'frontend', 'tsx', 'jsx'],
236
- 'compliance-security': ['security', 'compliance', 'audit', 'gdpr', 'encryption'],
237
- 'cost-optimization': ['cost', 'performance', 'optimization', 'billing', 'pricing']
238
- };
239
-
240
- const allText = `${intent} ${constraints} ${data}`;
241
-
242
- for (const [category, keywords] of Object.entries(categories)) {
243
- if (keywords.some(keyword => allText.includes(keyword))) {
244
- return category;
245
- }
246
- }
247
-
248
- return 'general'; // Default category
249
- }
250
-
251
- /**
252
- * Gather best examples from successful pattern usage
253
- *
254
- * @param {string} patternId - Pattern identifier
255
- * @returns {Promise<Array>} Best examples
256
- */
257
- async gatherBestExamples(patternId) {
258
- const query = `
259
- SELECT
260
- pu.context,
261
- pu.used_at,
262
- u."User_Display_Name" as user_name,
263
- p.project_name
264
- FROM rapport.pattern_usage pu
265
- JOIN "Users" u ON pu.email_address = u."Email_Address"
266
- JOIN rapport.patterns pat ON pu.pattern_id = pat.pattern_id
267
- JOIN rapport.projects p ON pat.project_id = p.project_id
268
- WHERE pu.pattern_id = $1
269
- AND pu.success = true
270
- ORDER BY pu.used_at DESC
271
- LIMIT 5
272
- `;
273
-
274
- const result = await executeQuery(query, [patternId]);
275
-
276
- return result.rows.map(row => ({
277
- code: row.context.code || 'No code example available',
278
- description: row.context.description || 'Successful pattern application',
279
- project: row.project_name,
280
- user: row.user_name,
281
- timestamp: row.used_at
282
- }));
283
- }
284
-
285
- /**
286
- * Gather anti-patterns from failed usage
287
- *
288
- * @param {string} patternId - Pattern identifier
289
- * @returns {Promise<Array>} Anti-patterns
290
- */
291
- async gatherAntiPatterns(patternId) {
292
- const query = `
293
- SELECT
294
- pu.context,
295
- pu.used_at,
296
- u."User_Display_Name" as user_name
297
- FROM rapport.pattern_usage pu
298
- JOIN "Users" u ON pu.email_address = u."Email_Address"
299
- WHERE pu.pattern_id = $1
300
- AND pu.success = false
301
- ORDER BY pu.used_at DESC
302
- LIMIT 5
303
- `;
304
-
305
- const result = await executeQuery(query, [patternId]);
306
-
307
- return result.rows.map(row => ({
308
- description: row.context.error || 'Pattern application failed',
309
- context: row.context.failureReason || 'Unknown failure reason',
310
- user: row.user_name,
311
- timestamp: row.used_at
312
- }));
313
- }
314
-
315
- /**
316
- * Get measurable benefit data (cost savings, performance improvements)
317
- *
318
- * @param {string} patternId - Pattern identifier
319
- * @returns {Promise<Object>} Measurable benefit
320
- */
321
- async getMeasurableBenefit(patternId) {
322
- const query = `
323
- SELECT
324
- pattern_data->'benefit' as benefit_data
325
- FROM rapport.patterns
326
- WHERE pattern_id = $1
327
- `;
328
-
329
- const result = await executeQuery(query, [patternId]);
330
-
331
- if (result.rows.length > 0 && result.rows[0].benefit_data) {
332
- return result.rows[0].benefit_data;
333
- }
334
-
335
- // Default benefit if not recorded
336
- return {
337
- type: 'team_efficiency',
338
- description: 'Proven pattern with high success rate across multiple teams',
339
- quantified: false
340
- };
341
- }
342
-
343
- /**
344
- * Generate proposed enforcement rule for the pattern
345
- *
346
- * @param {Object} pattern - Pattern details
347
- * @returns {string} Enforcement rule
348
- */
349
- generateEnforcementRule(pattern) {
350
- const constraints = Array.isArray(pattern.constraints) ? pattern.constraints : [];
351
- const outcomeCriteria = Array.isArray(pattern.outcome_criteria) ? pattern.outcome_criteria : [];
352
-
353
- let enforcement = `## Enforcement Rule\n\n`;
354
- enforcement += `**Intent**: ${pattern.intent}\n\n`;
355
-
356
- if (constraints.length > 0) {
357
- enforcement += `### Required Constraints\n\n`;
358
- constraints.forEach((constraint, idx) => {
359
- enforcement += `${idx + 1}. ${constraint}\n`;
360
- });
361
- enforcement += `\n`;
362
- }
363
-
364
- if (outcomeCriteria.length > 0) {
365
- enforcement += `### Success Criteria\n\n`;
366
- outcomeCriteria.forEach((criteria, idx) => {
367
- enforcement += `${idx + 1}. ${criteria}\n`;
368
- });
369
- enforcement += `\n`;
370
- }
371
-
372
- enforcement += `### Validation\n\n`;
373
- enforcement += `This pattern should be automatically validated during code review and CI/CD pipeline.\n`;
374
-
375
- return enforcement;
376
- }
377
-
378
- /**
379
- * Generate markdown standard document
380
- *
381
- * @param {Object} candidate - Curation candidate
382
- * @returns {string} Standard document in markdown
383
- */
384
- generateStandardDocument(candidate) {
385
- const { name, category, evidence, pattern, examples, antiPatterns, proposedEnforcement } = candidate;
386
-
387
- let doc = `# ${name}\n\n`;
388
- doc += `**Status**: Candidate (from Rapport curation)\n`;
389
- doc += `**Category**: ${category}\n`;
390
- doc += `**Date**: ${new Date().toISOString()}\n\n`;
391
-
392
- doc += `## Evidence\n\n`;
393
- doc += `- **Correlation**: ${(evidence.correlation * 100).toFixed(1)}%\n`;
394
- doc += `- **Projects**: ${evidence.projectCount}\n`;
395
- doc += `- **Developers**: ${evidence.developerCount}\n`;
396
- doc += `- **Sessions**: ${evidence.sessionCount}\n`;
397
- doc += `- **Benefit**: ${evidence.benefit.description || JSON.stringify(evidence.benefit)}\n\n`;
398
-
399
- doc += `## Pattern\n\n`;
400
- doc += `**Intent**: ${pattern.intent}\n\n`;
401
-
402
- if (pattern.constraints && pattern.constraints.length > 0) {
403
- doc += `**Constraints**:\n`;
404
- pattern.constraints.forEach((constraint, idx) => {
405
- doc += `${idx + 1}. ${constraint}\n`;
406
- });
407
- doc += `\n`;
408
- }
409
-
410
- if (pattern.outcome_criteria && pattern.outcome_criteria.length > 0) {
411
- doc += `**Outcome Criteria**:\n`;
412
- pattern.outcome_criteria.forEach((criteria, idx) => {
413
- doc += `${idx + 1}. ${criteria}\n`;
414
- });
415
- doc += `\n`;
416
- }
417
-
418
- doc += `## Examples\n\n`;
419
- if (examples.length > 0) {
420
- examples.forEach((example, idx) => {
421
- doc += `### Example ${idx + 1}: ${example.description}\n\n`;
422
- doc += `**Project**: ${example.project}\n`;
423
- doc += `**User**: ${example.user}\n\n`;
424
- doc += `\`\`\`javascript\n${example.code}\n\`\`\`\n\n`;
425
- });
426
- } else {
427
- doc += `No examples available.\n\n`;
428
- }
429
-
430
- doc += `## Anti-Patterns\n\n`;
431
- if (antiPatterns.length > 0) {
432
- antiPatterns.forEach((antiPattern, idx) => {
433
- doc += `${idx + 1}. ❌ **${antiPattern.description}**\n`;
434
- doc += ` - Context: ${antiPattern.context}\n`;
435
- doc += ` - Observed by: ${antiPattern.user}\n\n`;
436
- });
437
- } else {
438
- doc += `No anti-patterns recorded.\n\n`;
439
- }
440
-
441
- doc += proposedEnforcement;
442
- doc += `\n---\n\n`;
443
- doc += `**Generated by**: Rapport Curation Engine\n`;
444
- doc += `**Requires**: Human review before promotion to .equilateral-standards/\n`;
445
-
446
- return doc;
447
- }
448
-
449
- /**
450
- * Save curation candidate to database
451
- *
452
- * @param {string} patternId - Pattern identifier
453
- * @param {string} category - Proposed category
454
- * @param {Object} evidence - Evidence data
455
- * @param {string} standardDocument - Generated standard document
456
- * @returns {Promise<number>} Candidate ID
457
- */
458
- async saveCurationCandidate(patternId, category, evidence, standardDocument) {
459
- const query = `
460
- INSERT INTO rapport.curation_candidates
461
- (pattern_id, proposed_category, evidence, standard_document, status)
462
- VALUES
463
- ($1, $2, $3, $4, 'pending')
464
- RETURNING candidate_id
465
- `;
466
-
467
- const result = await executeQuery(query, [
468
- patternId,
469
- category,
470
- JSON.stringify(evidence),
471
- standardDocument
472
- ]);
473
-
474
- return result.rows[0].candidate_id;
475
- }
476
-
477
- /**
478
- * Notify stakeholders for human review
479
- *
480
- * @param {number} candidateId - Candidate ID
481
- * @param {Object} candidate - Curation candidate
482
- */
483
- async notifyForReview(candidateId, candidate) {
484
- console.log(`\n========================================`);
485
- console.log(`[CurationEngine] NEW CURATION CANDIDATE`);
486
- console.log(`========================================`);
487
- console.log(`Candidate ID: ${candidateId}`);
488
- console.log(`Pattern: ${candidate.name}`);
489
- console.log(`Category: ${candidate.category}`);
490
- console.log(`Evidence:`);
491
- console.log(` - Correlation: ${(candidate.evidence.correlation * 100).toFixed(1)}%`);
492
- console.log(` - Projects: ${candidate.evidence.projectCount}`);
493
- console.log(` - Developers: ${candidate.evidence.developerCount}`);
494
- console.log(` - Sessions: ${candidate.evidence.sessionCount}`);
495
- console.log(`\nAction Required: Review candidate for promotion to .equilateral-standards/`);
496
- console.log(`========================================\n`);
497
-
498
- // Send notifications to curators (admins with curation permissions)
499
- try {
500
- const curators = await this.getCurators();
501
-
502
- for (const curator of curators) {
503
- const preferences = await this.getUserPreferences(curator.email_address);
504
-
505
- await this.notificationService.sendNotification({
506
- type: 'curation_candidate',
507
- email: curator.email_address,
508
- preferences: preferences,
509
- data: {
510
- candidateId: candidateId,
511
- patternName: candidate.name,
512
- category: candidate.category,
513
- evidence: candidate.evidence,
514
- referenceType: 'curation_candidate',
515
- referenceId: String(candidateId)
516
- }
517
- });
518
- }
519
-
520
- console.log(`[CurationEngine] Notified ${curators.length} curators`);
521
- } catch (error) {
522
- console.error('[CurationEngine] Failed to send notifications:', error);
523
- // Don't throw - notification failure shouldn't block curation
524
- }
525
- }
526
-
527
- /**
528
- * Get users with curation permissions (Super Admins and Admins)
529
- *
530
- * @returns {Promise<Array>} List of curator emails
531
- */
532
- async getCurators() {
533
- const query = `
534
- SELECT DISTINCT u."Email_Address" as email_address
535
- FROM "Users" u
536
- LEFT JOIN "UserEntitlements" ue ON u."Email_Address" = ue."Email_Address"
537
- WHERE u."Super_Admin" = true OR ue."Admin" = true
538
- AND u."active" = true
539
- `;
540
-
541
- const result = await executeQuery(query);
542
- return result.rows;
543
- }
544
-
545
- /**
546
- * Get user notification preferences
547
- *
548
- * @param {string} email - User email
549
- * @returns {Promise<Object>} User preferences
550
- */
551
- async getUserPreferences(email) {
552
- const query = `SELECT rapport.get_notification_preferences($1) as preferences`;
553
- const result = await executeQuery(query, [email]);
554
- return result.rows[0]?.preferences || null;
555
- }
556
-
557
- /**
558
- * Refresh pattern metrics materialized view
559
- *
560
- * @returns {Promise<void>}
561
- */
562
- async refreshMetrics() {
563
- try {
564
- await executeQuery('REFRESH MATERIALIZED VIEW rapport.pattern_metrics');
565
- console.log('[CurationEngine] Pattern metrics refreshed');
566
- } catch (error) {
567
- console.error('[CurationEngine] Error refreshing metrics:', error);
568
- throw error;
569
- }
570
- }
571
-
572
- /**
573
- * Get all pending curation candidates
574
- *
575
- * @returns {Promise<Array>} Pending candidates
576
- */
577
- async getPendingCandidates() {
578
- const query = `
579
- SELECT
580
- candidate_id,
581
- pattern_id,
582
- proposed_category,
583
- evidence,
584
- standard_document,
585
- created_at
586
- FROM rapport.curation_candidates
587
- WHERE status = 'pending'
588
- ORDER BY created_at DESC
589
- `;
590
-
591
- const result = await executeQuery(query);
592
- return result.rows;
593
- }
594
-
595
- /**
596
- * Approve a curation candidate
597
- *
598
- * @param {number} candidateId - Candidate ID
599
- * @param {string} reviewedBy - Reviewer email
600
- * @returns {Promise<Object>} Approved candidate
601
- */
602
- async approveCandidate(candidateId, reviewedBy) {
603
- const query = `
604
- UPDATE rapport.curation_candidates
605
- SET
606
- status = 'approved',
607
- reviewed_by = $2,
608
- reviewed_at = NOW()
609
- WHERE candidate_id = $1
610
- RETURNING *
611
- `;
612
-
613
- const result = await executeQuery(query, [candidateId, reviewedBy]);
614
- return result.rows[0];
615
- }
616
-
617
- /**
618
- * Reject a curation candidate
619
- *
620
- * @param {number} candidateId - Candidate ID
621
- * @param {string} reviewedBy - Reviewer email
622
- * @returns {Promise<Object>} Rejected candidate
623
- */
624
- async rejectCandidate(candidateId, reviewedBy) {
625
- const query = `
626
- UPDATE rapport.curation_candidates
627
- SET
628
- status = 'rejected',
629
- reviewed_by = $2,
630
- reviewed_at = NOW()
631
- WHERE candidate_id = $1
632
- RETURNING *
633
- `;
634
-
635
- const result = await executeQuery(query, [candidateId, reviewedBy]);
636
- return result.rows[0];
637
- }
638
-
639
- /**
640
- * Evaluate all patterns for promotion
641
- *
642
- * @returns {Promise<Array>} Created candidates
643
- */
644
- async evaluateAllPatterns() {
645
- try {
646
- // Refresh metrics first
647
- await this.refreshMetrics();
648
-
649
- // Get all patterns that meet criteria
650
- const query = `
651
- SELECT pattern_id
652
- FROM rapport.pattern_metrics
653
- WHERE success_correlation >= $1
654
- AND project_count >= $2
655
- AND developer_count >= $3
656
- AND session_count >= $4
657
- AND pattern_id NOT IN (
658
- SELECT pattern_id
659
- FROM rapport.curation_candidates
660
- WHERE status IN ('pending', 'approved')
661
- )
662
- `;
663
-
664
- const result = await executeQuery(query, [
665
- this.config.minCorrelation,
666
- this.config.minProjects,
667
- this.config.minDevelopers,
668
- this.config.minSessions
669
- ]);
670
-
671
- const candidates = [];
672
- for (const row of result.rows) {
673
- const candidate = await this.evaluateForPromotion(row.pattern_id);
674
- if (candidate) {
675
- candidates.push(candidate);
676
- }
677
- }
678
-
679
- console.log(`[CurationEngine] Evaluated ${result.rows.length} patterns, created ${candidates.length} candidates`);
680
- return candidates;
681
- } catch (error) {
682
- console.error('[CurationEngine] Error evaluating all patterns:', error);
683
- throw error;
684
- }
685
- }
686
- }
687
-
688
- module.exports = { CurationEngine };