@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,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);