@equilateral_ai/mindmeld 3.5.3 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/hooks/session-start.js +312 -85
  2. package/package.json +20 -14
  3. package/scripts/init-project.js +9 -23
  4. package/src/client/dbShim.js +16 -0
  5. package/src/core/AuthManager.js +3 -2
  6. package/src/handlers/helpers/dbOperations.js +9 -46
  7. package/src/index.js +2 -217
  8. package/src/utils/piiMask.js +16 -0
  9. package/scripts/harvest.js +0 -601
  10. package/scripts/inject.js +0 -409
  11. package/scripts/mcp-bridge.js +0 -220
  12. package/scripts/repo-analyzer.js +0 -870
  13. package/scripts/standards.js +0 -285
  14. package/src/collaboration/CollaborationPrompt.js +0 -460
  15. package/src/core/AlertEngine.js +0 -813
  16. package/src/core/AlertNotifier.js +0 -363
  17. package/src/core/CorrelationAnalyzer.js +0 -931
  18. package/src/core/CrossReferenceEngine.js +0 -624
  19. package/src/core/CurationEngine.js +0 -688
  20. package/src/core/DeprecationScheduler.js +0 -183
  21. package/src/core/LoadBearingDetector.js +0 -242
  22. package/src/core/NotificationService.js +0 -1032
  23. package/src/core/RapportOrchestrator.js +0 -632
  24. package/src/core/RelevanceDetector.js +0 -694
  25. package/src/core/StandardLifecycle.js +0 -244
  26. package/src/core/StandardsIngestion.js +0 -991
  27. package/src/core/TeamLoadBearingDetector.js +0 -431
  28. package/src/core/parsers/adrParser.js +0 -479
  29. package/src/core/parsers/cursorRulesParser.js +0 -564
  30. package/src/core/parsers/eslintParser.js +0 -439
  31. package/src/database/dbOperations.js +0 -105
  32. package/src/handlers/activity/activityGetMe.js +0 -98
  33. package/src/handlers/activity/activityGetTeam.js +0 -175
  34. package/src/handlers/admin/adminSetup.js +0 -216
  35. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  36. package/src/handlers/alerts/alertsGet.js +0 -250
  37. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  38. package/src/handlers/analytics/coachingGet.js +0 -361
  39. package/src/handlers/analytics/convergenceGet.js +0 -236
  40. package/src/handlers/analytics/developerScoreGet.js +0 -137
  41. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  42. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  43. package/src/handlers/collaborators/collaboratorList.js +0 -82
  44. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  45. package/src/handlers/collaborators/inviteAccept.js +0 -122
  46. package/src/handlers/company/companyUsersDelete.js +0 -141
  47. package/src/handlers/company/companyUsersGet.js +0 -90
  48. package/src/handlers/company/companyUsersPost.js +0 -267
  49. package/src/handlers/company/companyUsersPut.js +0 -76
  50. package/src/handlers/context/contextGet.js +0 -57
  51. package/src/handlers/context/invariantsGet.js +0 -74
  52. package/src/handlers/context/loopsGet.js +0 -82
  53. package/src/handlers/context/notesCreate.js +0 -74
  54. package/src/handlers/context/purposeGet.js +0 -78
  55. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  56. package/src/handlers/correlations/correlationsGet.js +0 -93
  57. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  58. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  59. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  60. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  61. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  62. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  63. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  64. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  65. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  66. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  67. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  68. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  69. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  70. package/src/handlers/github/githubConnectionStatus.js +0 -49
  71. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  72. package/src/handlers/github/githubOAuthCallback.js +0 -178
  73. package/src/handlers/github/githubOAuthStart.js +0 -59
  74. package/src/handlers/github/githubPatternsReview.js +0 -76
  75. package/src/handlers/github/githubReposList.js +0 -105
  76. package/src/handlers/health/healthGet.js +0 -55
  77. package/src/handlers/helpers/auditLogger.js +0 -201
  78. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  79. package/src/handlers/helpers/decisionFrames.js +0 -29
  80. package/src/handlers/helpers/errorHandler.js +0 -49
  81. package/src/handlers/helpers/index.js +0 -138
  82. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  83. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  84. package/src/handlers/helpers/predictiveCache.js +0 -51
  85. package/src/handlers/helpers/projectAccess.js +0 -88
  86. package/src/handlers/helpers/responseUtil.js +0 -55
  87. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  88. package/src/handlers/mcp/mcpHandler.js +0 -569
  89. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  90. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  91. package/src/handlers/notifications/getPreferences.js +0 -84
  92. package/src/handlers/notifications/sendNotification.js +0 -170
  93. package/src/handlers/notifications/updatePreferences.js +0 -316
  94. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  95. package/src/handlers/patterns/patternUsagePost.js +0 -182
  96. package/src/handlers/patterns/patternViolationPost.js +0 -185
  97. package/src/handlers/projects/projectCreate.js +0 -248
  98. package/src/handlers/projects/projectDelete.js +0 -82
  99. package/src/handlers/projects/projectGet.js +0 -95
  100. package/src/handlers/projects/projectUpdate.js +0 -117
  101. package/src/handlers/reports/aiLeverage.js +0 -210
  102. package/src/handlers/reports/engineeringInvestment.js +0 -132
  103. package/src/handlers/reports/riskForecast.js +0 -206
  104. package/src/handlers/reports/standardsRoi.js +0 -254
  105. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  106. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  107. package/src/handlers/scheduled/generateAlerts.js +0 -135
  108. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  109. package/src/handlers/scheduled/refreshActivity.js +0 -21
  110. package/src/handlers/scheduled/scanCompliance.js +0 -334
  111. package/src/handlers/sessions/sessionEndPost.js +0 -180
  112. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  113. package/src/handlers/standards/catalogGet.js +0 -185
  114. package/src/handlers/standards/catalogSync.js +0 -120
  115. package/src/handlers/standards/discoveriesGet.js +0 -89
  116. package/src/handlers/standards/projectStandardsGet.js +0 -129
  117. package/src/handlers/standards/projectStandardsPut.js +0 -151
  118. package/src/handlers/standards/standardsAuditGet.js +0 -65
  119. package/src/handlers/standards/standardsParseUpload.js +0 -149
  120. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  121. package/src/handlers/standards/standardsTransition.js +0 -161
  122. package/src/handlers/stripe/addonManagePost.js +0 -240
  123. package/src/handlers/stripe/billingPortalPost.js +0 -93
  124. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  125. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  126. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  127. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  128. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  129. package/src/handlers/stripe/webhookPost.js +0 -482
  130. package/src/handlers/user/apiTokenCreate.js +0 -71
  131. package/src/handlers/user/apiTokenList.js +0 -64
  132. package/src/handlers/user/userSplashAck.js +0 -91
  133. package/src/handlers/user/userSplashGet.js +0 -211
  134. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  135. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  136. package/src/handlers/users/userEntitlementsGet.js +0 -89
  137. package/src/handlers/users/userGet.js +0 -118
  138. package/src/handlers/users/userProfilePut.js +0 -77
  139. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,405 +0,0 @@
1
- /**
2
- * Standards Relevant Post Handler
3
- *
4
- * Returns top 10 relevant standards for a project based on detected characteristics.
5
- * The session-start hook calls this endpoint with locally-detected project characteristics;
6
- * the handler maps them to categories, queries the database, ranks results, and returns
7
- * the most relevant standards for injection into the AI coding session.
8
- *
9
- * POST /api/standards/relevant
10
- * Auth: Cognito JWT required
11
- * Body: { characteristics, projectId?, preferences? }
12
- */
13
-
14
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, getPredictedStandards, logStandardsActivation, createFrame } = require('./helpers');
15
-
16
- /**
17
- * Category weights for relevance scoring
18
- */
19
- const CATEGORY_WEIGHTS = {
20
- // Code standard categories
21
- 'serverless-saas-aws': 1.0,
22
- 'frontend-development': 1.0,
23
- 'database': 0.9,
24
- 'backend': 0.9,
25
- 'compliance-security': 0.9,
26
- 'deployment': 0.8,
27
- 'testing': 0.7,
28
- 'real-time-systems': 0.7,
29
- 'well-architected': 0.7,
30
- 'cost-optimization': 0.7,
31
- 'multi-agent-orchestration': 0.1, // Infrastructure config, rarely needed in coding sessions
32
- // Business domains — moderate weights, boosted by recency when relevant
33
- 'ip-strategy': 0.6,
34
- 'architecture-decisions': 0.8,
35
- 'go-to-market': 0.6,
36
- 'operations': 0.5,
37
- 'legal-process': 0.5,
38
- 'finance': 0.5,
39
- 'communication': 0.4,
40
- 'product-strategy': 0.6,
41
- 'investor-relations': 0.4,
42
- };
43
-
44
- /**
45
- * Map project characteristics to relevant standard categories
46
- */
47
- function mapCharacteristicsToCategories(characteristics) {
48
- const categories = new Set();
49
-
50
- if (characteristics.hasLambda || characteristics.hasSAM) {
51
- categories.add('serverless-saas-aws');
52
- categories.add('cost-optimization');
53
- }
54
- if (characteristics.hasReact) {
55
- categories.add('frontend-development');
56
- }
57
- if (characteristics.hasDatabase) {
58
- categories.add('database');
59
- }
60
- if (characteristics.hasMultiAgent) {
61
- categories.add('multi-agent-orchestration');
62
- }
63
- if (characteristics.hasAPI) {
64
- categories.add('backend');
65
- }
66
- if (characteristics.hasTests) {
67
- categories.add('testing');
68
- }
69
- if (characteristics.hasSAM || characteristics.hasLambda || characteristics.hasAPI) {
70
- categories.add('deployment');
71
- }
72
-
73
- // Always relevant
74
- categories.add('compliance-security');
75
- categories.add('well-architected');
76
-
77
- return Array.from(categories);
78
- }
79
-
80
- /**
81
- * Rank standards by relevance score
82
- */
83
- function rankStandards(standards, recentCategories) {
84
- return standards.map(standard => {
85
- let score = 0;
86
-
87
- // Base score from correlation
88
- score += (standard.correlation || 1.0) * 40;
89
-
90
- // Maturity score (includes business invariant lifecycle values)
91
- const maturityScores = { enforced: 30, reinforced: 25, validated: 20, solidified: 15, recommended: 10, provisional: 5 };
92
- score += maturityScores[standard.maturity] || 0;
93
-
94
- // Category weight
95
- const categoryWeight = CATEGORY_WEIGHTS[standard.category] || 0.5;
96
- score += categoryWeight * 20;
97
-
98
- // File applicability bonus
99
- if (standard.applicable_files && standard.applicable_files.length > 0) {
100
- score += 5;
101
- }
102
-
103
- // Cost impact bonus (critical patterns)
104
- if (standard.cost_impact && standard.cost_impact.severity === 'critical') {
105
- score += 10;
106
- }
107
-
108
- // Anti-patterns bonus (important to know what NOT to do)
109
- if (standard.anti_patterns) {
110
- const apCount = Array.isArray(standard.anti_patterns)
111
- ? standard.anti_patterns.length
112
- : Object.keys(standard.anti_patterns).length;
113
- if (apCount > 0) score += 5;
114
- }
115
-
116
- // Workflow bonus — workflows are high-value procedural knowledge
117
- const isWorkflow = (standard.rule && standard.rule.startsWith('WORKFLOW:'))
118
- || (Array.isArray(standard.keywords) && standard.keywords.includes('workflow'));
119
- if (isWorkflow) score += 10;
120
-
121
- // Rationale bonus — business invariants with documented reasoning
122
- if (standard.rationale) score += 5;
123
-
124
- // Recency bonus — boost categories the user has been working in recently
125
- // Scaled by category weight to prevent feedback loops (low-weight categories
126
- // that got injected shouldn't bootstrap themselves back into the top 10)
127
- if (recentCategories && recentCategories[standard.category]) {
128
- const usageCount = recentCategories[standard.category];
129
- let rawBonus;
130
- if (usageCount >= 8) rawBonus = 25;
131
- else if (usageCount >= 4) rawBonus = 18;
132
- else rawBonus = 10;
133
- score += rawBonus * categoryWeight;
134
- }
135
-
136
- // Recency decay: patterns not seen in 30+ days get penalized
137
- if (standard.last_seen_at) {
138
- const daysSinceLastSeen = (Date.now() - new Date(standard.last_seen_at).getTime()) / (1000 * 60 * 60 * 24);
139
- if (daysSinceLastSeen > 30) {
140
- score *= 0.5;
141
- }
142
- }
143
-
144
- // Frequency boost: high-occurrence patterns are more valuable
145
- const occurrenceCount = standard.occurrence_count || 1;
146
- if (occurrenceCount > 50) {
147
- score *= 1.5;
148
- } else if (occurrenceCount > 10) {
149
- score *= 1.2;
150
- }
151
-
152
- return {
153
- ...standard,
154
- relevance_score: Math.round(score * 10) / 10
155
- };
156
- }).sort((a, b) => {
157
- // Load-bearing standards always sort to the top
158
- const aLB = a.load_bearing ? 1 : 0;
159
- const bLB = b.load_bearing ? 1 : 0;
160
- if (bLB !== aLB) return bLB - aLB;
161
- return b.relevance_score - a.relevance_score;
162
- });
163
- }
164
-
165
- /**
166
- * Apply user preferences to filter standards
167
- */
168
- function applyPreferences(standards, preferences) {
169
- if (!preferences) return standards;
170
-
171
- const enabledCategories = preferences.enabled_categories || {};
172
- const standardOverrides = preferences.standard_overrides || {};
173
-
174
- return standards.filter(standard => {
175
- const category = standard.category;
176
- const standardPath = `${category}/${standard.element}`;
177
-
178
- // Individual override takes priority
179
- if (standardPath in standardOverrides) {
180
- return standardOverrides[standardPath] === true;
181
- }
182
-
183
- // Category-level setting
184
- if (category in enabledCategories) {
185
- return enabledCategories[category] === true;
186
- }
187
-
188
- // Default: include
189
- return true;
190
- });
191
- }
192
-
193
- async function getRelevantStandards({ body, requestContext }) {
194
- const email = requestContext.authorizer?.claims?.email
195
- || requestContext.authorizer?.jwt?.claims?.email;
196
-
197
- if (!email) {
198
- return createErrorResponse(401, 'Authentication required');
199
- }
200
-
201
- // Verify active subscription and resolve company_id for tenant isolation
202
- const subResult = await executeQuery(`
203
- SELECT c.subscription_tier, c.subscription_status, ue.company_id
204
- FROM rapport.users u
205
- JOIN rapport.clients c ON u.client_id = c.client_id
206
- LEFT JOIN rapport.user_entitlements ue ON u.email_address = ue.email_address
207
- WHERE u.email_address = $1
208
- LIMIT 1
209
- `, [email]);
210
-
211
- let companyId = null;
212
- if (subResult.rows.length > 0) {
213
- const { subscription_tier, subscription_status } = subResult.rows[0];
214
- companyId = subResult.rows[0].company_id;
215
- if (!subscription_tier || subscription_tier === 'free') {
216
- return createErrorResponse(403, 'Active MindMeld subscription required. Subscribe at app.mindmeld.dev');
217
- }
218
- if (subscription_status === 'canceled') {
219
- return createErrorResponse(403, 'Subscription canceled. Resubscribe at app.mindmeld.dev');
220
- }
221
- }
222
-
223
- // Parse request body
224
- let requestBody;
225
- try {
226
- requestBody = typeof body === 'string' ? JSON.parse(body) : body;
227
- } catch (e) {
228
- return createErrorResponse(400, 'Invalid request body');
229
- }
230
-
231
- const { characteristics, preferences } = requestBody || {};
232
-
233
- if (!characteristics || typeof characteristics !== 'object') {
234
- return createErrorResponse(400, 'characteristics object is required');
235
- }
236
-
237
- // Map characteristics to categories
238
- const categories = mapCharacteristicsToCategories(characteristics);
239
-
240
- if (categories.length === 0) {
241
- return createSuccessResponse({
242
- standards: [],
243
- categories: [],
244
- total_matched: 0
245
- }, 'No relevant categories detected');
246
- }
247
-
248
- // Query recent session categories first (fast), then merge into categories for main query
249
- const recentCategories = {};
250
- try {
251
- const recencyResult = await executeQuery(`
252
- SELECT sp.category, COUNT(*) as usage_count
253
- FROM rapport.session_standards ss
254
- JOIN rapport.sessions s ON s.session_id = ss.session_id
255
- JOIN rapport.standards_patterns sp ON sp.pattern_id = ss.standard_id
256
- WHERE s.email_address = $1
257
- AND s.started_at >= NOW() - INTERVAL '7 days'
258
- GROUP BY sp.category
259
- ORDER BY usage_count DESC
260
- LIMIT 5
261
- `, [email]);
262
- for (const row of recencyResult.rows) {
263
- recentCategories[row.category] = parseInt(row.usage_count, 10);
264
- }
265
- } catch (err) {
266
- console.error('[standardsRelevant] Recency query failed:', err.message);
267
- }
268
-
269
- // Merge recency categories into query — recent activity should always be represented
270
- for (const category of Object.keys(recentCategories)) {
271
- if (!categories.includes(category)) {
272
- categories.push(category);
273
- }
274
- }
275
-
276
- // Query standards — tenant-isolated via get_effective_standards()
277
- const maturityList = ['enforced', 'validated', 'recommended', 'provisional', 'solidified', 'reinforced'];
278
- const result = await executeQuery(`
279
- SELECT * FROM rapport.get_effective_standards($1, $2::varchar[], $3::varchar[])
280
- ORDER BY
281
- CASE
282
- WHEN maturity = 'enforced' THEN 1
283
- WHEN maturity = 'reinforced' THEN 2
284
- WHEN maturity = 'validated' THEN 3
285
- WHEN maturity = 'solidified' THEN 4
286
- WHEN maturity = 'recommended' THEN 5
287
- ELSE 6
288
- END,
289
- correlation DESC
290
- `, [companyId, categories, maturityList]);
291
-
292
- // Predictive cache: fetch standards with >80% activation rate for this project
293
- let predictedStandards = [];
294
- const projectId = requestBody.projectId;
295
- if (projectId) {
296
- try {
297
- const predicted = await getPredictedStandards(projectId);
298
- if (predicted.length > 0) {
299
- const predictedIds = new Set(predicted.map(p => p.standard_id));
300
- // Pull predicted standards from query results and mark them as pre-cached
301
- predictedStandards = result.rows
302
- .filter(s => predictedIds.has(s.pattern_id || s.standard_id))
303
- .map(s => ({ ...s, relevance_score: 100, predicted: true }));
304
- }
305
- } catch (err) {
306
- console.error('[standardsRelevant] Predictive cache lookup failed:', err.message);
307
- }
308
- }
309
-
310
- // Rank by relevance with recency boost
311
- let ranked = rankStandards(result.rows, recentCategories);
312
-
313
- // Deduplicate by element name (same rule can be ingested with different path prefixes)
314
- const seenElements = new Set();
315
- ranked = ranked.filter(standard => {
316
- const key = standard.element;
317
- if (seenElements.has(key)) return false;
318
- seenElements.add(key);
319
- return true;
320
- });
321
-
322
- // Apply preferences if provided
323
- if (preferences) {
324
- ranked = applyPreferences(ranked, preferences);
325
- }
326
-
327
- // Return top 10 with diversity caps:
328
- // - Max 2 per category (prevents single-category saturation)
329
- // - Max 1 per standard title (prevents same-file rule pairs wasting slots)
330
- // Predicted standards are included first (they skip scoring)
331
- const MAX_PER_CATEGORY = 2;
332
- const MAX_PER_TITLE = 1;
333
- const top = [];
334
- const categoryCounts = {};
335
- const titleCounts = {};
336
- const seenPredicted = new Set();
337
-
338
- // Add predicted standards first — they have proven relevance
339
- for (const standard of predictedStandards) {
340
- const cat = standard.category;
341
- const title = standard.title || standard.element;
342
- const key = standard.element;
343
- categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
344
- titleCounts[title] = (titleCounts[title] || 0) + 1;
345
- seenPredicted.add(key);
346
- if (categoryCounts[cat] <= MAX_PER_CATEGORY && titleCounts[title] <= MAX_PER_TITLE) {
347
- top.push(standard);
348
- if (top.length >= 10) break;
349
- }
350
- }
351
-
352
- // Fill remaining slots from scored standards
353
- for (const standard of ranked) {
354
- if (top.length >= 10) break;
355
- if (seenPredicted.has(standard.element)) continue;
356
- const cat = standard.category;
357
- const title = standard.title || standard.element;
358
- categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
359
- titleCounts[title] = (titleCounts[title] || 0) + 1;
360
- if (categoryCounts[cat] <= MAX_PER_CATEGORY && titleCounts[title] <= MAX_PER_TITLE) {
361
- top.push(standard);
362
- }
363
- }
364
-
365
- // Log activated standards for future predictions
366
- if (projectId && top.length > 0) {
367
- const activatedIds = top
368
- .map(s => s.pattern_id || s.standard_id)
369
- .filter(id => id != null);
370
- logStandardsActivation(projectId, activatedIds).catch(err => {
371
- console.error('[standardsRelevant] Activation logging failed:', err.message);
372
- });
373
- }
374
-
375
- // Track decision frame for this standards selection
376
- let frameId = null;
377
- if (projectId && top.length > 0) {
378
- try {
379
- const standardIds = top
380
- .map(s => s.pattern_id || s.standard_id)
381
- .filter(id => id != null);
382
- const avgScore = top.reduce((sum, s) => sum + (s.relevance_score || 0), 0) / top.length;
383
- const confidence = Math.min(avgScore / 100, 1);
384
- const frame = await createFrame({
385
- projectId,
386
- sessionId: requestBody.sessionId || null,
387
- standardIds,
388
- confidence,
389
- context: { categories, characteristics, total_matched: result.rows.length }
390
- });
391
- frameId = frame.frame_id;
392
- } catch (err) {
393
- console.error('[standardsRelevant] Decision frame creation failed:', err.message);
394
- }
395
- }
396
-
397
- return createSuccessResponse({
398
- standards: top,
399
- categories,
400
- total_matched: result.rows.length,
401
- frame_id: frameId
402
- }, 'Relevant standards retrieved');
403
- }
404
-
405
- exports.handler = wrapHandler(getRelevantStandards);
@@ -1,161 +0,0 @@
1
- /**
2
- * Standards Transition Handler
3
- * Executes lifecycle state transitions on standards and discoveries
4
- *
5
- * POST /api/standards/transition
6
- * Body: { standard_id, action: 'approve'|'reject'|'observe'|'disable'|'deprecate'|'delete'|'enable'|'cancel_deprecation', reason?, reason_code? }
7
- * Auth: Cognito JWT required
8
- *
9
- * For discovery IDs (from onboarding_discoveries table):
10
- * approve → creates pattern + marks discovery approved
11
- * reject → marks discovery rejected
12
- * observe → marks discovery as observing (deferred review)
13
- */
14
-
15
- const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
16
- const { StandardLifecycle } = require('./core/StandardLifecycle');
17
-
18
- const lifecycle = new StandardLifecycle();
19
-
20
- const DISCOVERY_ACTIONS = ['approve', 'reject', 'observe'];
21
-
22
- /**
23
- * Handle transitions for onboarding discoveries
24
- */
25
- async function transitionDiscovery(discoveryId, action, email, reason, reasonCode) {
26
- const discovery = await executeQuery(`
27
- SELECT discovery_id, project_id, discovery_type, pattern_name, pattern_description, confidence, evidence, status
28
- FROM rapport.onboarding_discoveries
29
- WHERE discovery_id = $1
30
- `, [discoveryId]);
31
-
32
- if (discovery.rowCount === 0) {
33
- return null; // Not a discovery either
34
- }
35
-
36
- const row = discovery.rows[0];
37
- const oldStatus = row.status;
38
-
39
- if (action === 'approve') {
40
- // Create pattern from discovery
41
- const patternId = `pat_${row.project_id}_${Date.now()}`;
42
- await executeQuery(`
43
- INSERT INTO rapport.patterns (
44
- pattern_id, project_id, intent, constraints, outcome_criteria,
45
- maturity, discovered_by, pattern_data
46
- ) VALUES ($1, $2, $3, $4, $5, 'provisional', $6, $7)
47
- `, [
48
- patternId,
49
- row.project_id,
50
- row.pattern_name,
51
- JSON.stringify([row.pattern_description || '']),
52
- JSON.stringify([`Discovered via onboarding: ${row.discovery_type}`]),
53
- email,
54
- JSON.stringify({
55
- source: 'discovery_review',
56
- discovery_type: row.discovery_type,
57
- evidence: row.evidence
58
- })
59
- ]);
60
-
61
- await executeQuery(`
62
- UPDATE rapport.onboarding_discoveries
63
- SET status = 'approved', reviewed_at = NOW(), updated_at = NOW(), pattern_id = $1
64
- WHERE discovery_id = $2
65
- `, [patternId, discoveryId]);
66
-
67
- return { old_state: oldStatus, new_state: 'approved', pattern_id: patternId };
68
- }
69
-
70
- if (action === 'reject') {
71
- await executeQuery(`
72
- UPDATE rapport.onboarding_discoveries
73
- SET status = 'rejected', reviewed_at = NOW(), updated_at = NOW(), reason = $1, reason_code = $2
74
- WHERE discovery_id = $3
75
- `, [reason || null, reasonCode || null, discoveryId]);
76
-
77
- return { old_state: oldStatus, new_state: 'rejected' };
78
- }
79
-
80
- if (action === 'observe') {
81
- await executeQuery(`
82
- UPDATE rapport.onboarding_discoveries
83
- SET status = 'observing', updated_at = NOW()
84
- WHERE discovery_id = $1
85
- `, [discoveryId]);
86
-
87
- return { old_state: oldStatus, new_state: 'observing' };
88
- }
89
-
90
- return null;
91
- }
92
-
93
- async function transitionStandard({ body, requestContext }) {
94
- try {
95
- const email = requestContext.authorizer?.claims?.email || requestContext.authorizer?.jwt?.claims?.email;
96
-
97
- if (!email) {
98
- return createErrorResponse(401, 'Authentication required');
99
- }
100
-
101
- const { standard_id: id, action, reason, reason_code: reasonCode } = body || {};
102
-
103
- if (!id) {
104
- return createErrorResponse(400, 'standard_id is required');
105
- }
106
-
107
- if (!action) {
108
- return createErrorResponse(400, 'action is required', {
109
- valid_actions: ['propose', 'approve', 'reject', 'observe', 'disable', 'deprecate', 'delete', 'enable', 'cancel_deprecation']
110
- });
111
- }
112
-
113
- // Try standard lifecycle transition first
114
- try {
115
- const result = await lifecycle.transition(id, action, email, reason);
116
-
117
- return createSuccessResponse({
118
- standard_id: result.standard_id,
119
- old_state: result.old_state,
120
- new_state: result.new_state,
121
- action: result.action,
122
- audit_entry: result.audit_entry
123
- }, `Standard transitioned from '${result.old_state}' to '${result.new_state}'`);
124
- } catch (lifecycleError) {
125
- // If pattern not found and action is valid for discoveries, try discovery transition
126
- if (lifecycleError.message.includes('not found') && DISCOVERY_ACTIONS.includes(action)) {
127
- const discoveryResult = await transitionDiscovery(id, action, email, reason, reasonCode);
128
-
129
- if (discoveryResult) {
130
- return createSuccessResponse({
131
- standard_id: id,
132
- old_state: discoveryResult.old_state,
133
- new_state: discoveryResult.new_state,
134
- action,
135
- pattern_id: discoveryResult.pattern_id || null
136
- }, `Discovery transitioned from '${discoveryResult.old_state}' to '${discoveryResult.new_state}'`);
137
- }
138
- }
139
-
140
- // Re-throw if not handled
141
- throw lifecycleError;
142
- }
143
-
144
- } catch (error) {
145
- console.error('Standards Transition Error:', error);
146
-
147
- if (error.message.includes('Invalid action')) {
148
- return createErrorResponse(400, error.message);
149
- }
150
- if (error.message.includes('not found')) {
151
- return createErrorResponse(404, error.message);
152
- }
153
- if (error.message.includes('Cannot perform')) {
154
- return createErrorResponse(409, error.message);
155
- }
156
-
157
- return createErrorResponse(500, 'Failed to transition standard');
158
- }
159
- }
160
-
161
- exports.handler = wrapHandler(transitionStandard);