@equilateral_ai/mindmeld 3.5.0 → 3.5.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.
- package/hooks/pre-compact.js +269 -21
- package/hooks/session-start.js +139 -34
- package/package.json +2 -1
- package/scripts/auth-login.js +45 -8
- package/src/core/StandardsIngestion.js +3 -1
- package/src/handlers/collaborators/collaboratorList.js +4 -10
- package/src/handlers/correlations/correlationsProjectGet.js +4 -13
- package/src/handlers/github/githubDiscoverPatterns.js +4 -8
- package/src/handlers/github/githubPatternsReview.js +4 -8
- package/src/handlers/helpers/decisionFrames.js +29 -0
- package/src/handlers/helpers/index.js +14 -0
- package/src/handlers/helpers/mindmeldMcpCore.js +566 -57
- package/src/handlers/helpers/predictiveCache.js +51 -0
- package/src/handlers/helpers/projectAccess.js +88 -0
- package/src/handlers/mcp/mindmeldMcpStreamHandler.js +113 -14
- package/src/handlers/standards/discoveriesGet.js +4 -8
- package/src/handlers/standards/projectStandardsGet.js +5 -11
- package/src/handlers/standards/projectStandardsPut.js +34 -14
- package/src/handlers/standards/standardsParseUpload.js +4 -8
- package/src/handlers/standards/standardsRelevantPost.js +126 -29
|
@@ -11,12 +11,13 @@
|
|
|
11
11
|
* Body: { characteristics, projectId?, preferences? }
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse } = require('./helpers');
|
|
14
|
+
const { wrapHandler, executeQuery, createSuccessResponse, createErrorResponse, getPredictedStandards, logStandardsActivation, createFrame } = require('./helpers');
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
17
|
* Category weights for relevance scoring
|
|
18
18
|
*/
|
|
19
19
|
const CATEGORY_WEIGHTS = {
|
|
20
|
+
// Code standard categories
|
|
20
21
|
'serverless-saas-aws': 1.0,
|
|
21
22
|
'frontend-development': 1.0,
|
|
22
23
|
'database': 0.9,
|
|
@@ -28,6 +29,16 @@ const CATEGORY_WEIGHTS = {
|
|
|
28
29
|
'well-architected': 0.7,
|
|
29
30
|
'cost-optimization': 0.7,
|
|
30
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,
|
|
31
42
|
};
|
|
32
43
|
|
|
33
44
|
/**
|
|
@@ -76,8 +87,8 @@ function rankStandards(standards, recentCategories) {
|
|
|
76
87
|
// Base score from correlation
|
|
77
88
|
score += (standard.correlation || 1.0) * 40;
|
|
78
89
|
|
|
79
|
-
// Maturity score
|
|
80
|
-
const maturityScores = { enforced: 30, validated: 20, recommended: 10, provisional: 5 };
|
|
90
|
+
// Maturity score (includes business invariant lifecycle values)
|
|
91
|
+
const maturityScores = { enforced: 30, reinforced: 25, validated: 20, solidified: 15, recommended: 10, provisional: 5 };
|
|
81
92
|
score += maturityScores[standard.maturity] || 0;
|
|
82
93
|
|
|
83
94
|
// Category weight
|
|
@@ -107,6 +118,9 @@ function rankStandards(standards, recentCategories) {
|
|
|
107
118
|
|| (Array.isArray(standard.keywords) && standard.keywords.includes('workflow'));
|
|
108
119
|
if (isWorkflow) score += 10;
|
|
109
120
|
|
|
121
|
+
// Rationale bonus — business invariants with documented reasoning
|
|
122
|
+
if (standard.rationale) score += 5;
|
|
123
|
+
|
|
110
124
|
// Recency bonus — boost categories the user has been working in recently
|
|
111
125
|
// Scaled by category weight to prevent feedback loops (low-weight categories
|
|
112
126
|
// that got injected shouldn't bootstrap themselves back into the top 10)
|
|
@@ -119,11 +133,33 @@ function rankStandards(standards, recentCategories) {
|
|
|
119
133
|
score += rawBonus * categoryWeight;
|
|
120
134
|
}
|
|
121
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
|
+
|
|
122
152
|
return {
|
|
123
153
|
...standard,
|
|
124
154
|
relevance_score: Math.round(score * 10) / 10
|
|
125
155
|
};
|
|
126
|
-
}).sort((a, b) =>
|
|
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
|
+
});
|
|
127
163
|
}
|
|
128
164
|
|
|
129
165
|
/**
|
|
@@ -162,17 +198,20 @@ async function getRelevantStandards({ body, requestContext }) {
|
|
|
162
198
|
return createErrorResponse(401, 'Authentication required');
|
|
163
199
|
}
|
|
164
200
|
|
|
165
|
-
// Verify active subscription
|
|
201
|
+
// Verify active subscription and resolve company_id for tenant isolation
|
|
166
202
|
const subResult = await executeQuery(`
|
|
167
|
-
SELECT c.subscription_tier, c.subscription_status
|
|
203
|
+
SELECT c.subscription_tier, c.subscription_status, ue.company_id
|
|
168
204
|
FROM rapport.users u
|
|
169
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
|
|
170
207
|
WHERE u.email_address = $1
|
|
171
208
|
LIMIT 1
|
|
172
209
|
`, [email]);
|
|
173
210
|
|
|
211
|
+
let companyId = null;
|
|
174
212
|
if (subResult.rows.length > 0) {
|
|
175
213
|
const { subscription_tier, subscription_status } = subResult.rows[0];
|
|
214
|
+
companyId = subResult.rows[0].company_id;
|
|
176
215
|
if (!subscription_tier || subscription_tier === 'free') {
|
|
177
216
|
return createErrorResponse(403, 'Active MindMeld subscription required. Subscribe at app.mindmeld.dev');
|
|
178
217
|
}
|
|
@@ -234,33 +273,39 @@ async function getRelevantStandards({ body, requestContext }) {
|
|
|
234
273
|
}
|
|
235
274
|
}
|
|
236
275
|
|
|
237
|
-
// Query standards
|
|
276
|
+
// Query standards — tenant-isolated via get_effective_standards()
|
|
277
|
+
const maturityList = ['enforced', 'validated', 'recommended', 'provisional', 'solidified', 'reinforced'];
|
|
238
278
|
const result = await executeQuery(`
|
|
239
|
-
SELECT
|
|
240
|
-
pattern_id,
|
|
241
|
-
element,
|
|
242
|
-
title,
|
|
243
|
-
rule,
|
|
244
|
-
category,
|
|
245
|
-
keywords,
|
|
246
|
-
correlation,
|
|
247
|
-
maturity,
|
|
248
|
-
applicable_files,
|
|
249
|
-
anti_patterns,
|
|
250
|
-
examples,
|
|
251
|
-
cost_impact,
|
|
252
|
-
source
|
|
253
|
-
FROM rapport.standards_patterns
|
|
254
|
-
WHERE category = ANY($1::varchar[])
|
|
255
|
-
AND maturity IN ('enforced', 'validated', 'recommended')
|
|
279
|
+
SELECT * FROM rapport.get_effective_standards($1, $2::varchar[], $3::varchar[])
|
|
256
280
|
ORDER BY
|
|
257
281
|
CASE
|
|
258
282
|
WHEN maturity = 'enforced' THEN 1
|
|
259
|
-
WHEN maturity = '
|
|
260
|
-
|
|
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
|
|
261
288
|
END,
|
|
262
289
|
correlation DESC
|
|
263
|
-
`, [categories]);
|
|
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
|
+
}
|
|
264
309
|
|
|
265
310
|
// Rank by relevance with recency boost
|
|
266
311
|
let ranked = rankStandards(result.rows, recentCategories);
|
|
@@ -282,26 +327,78 @@ async function getRelevantStandards({ body, requestContext }) {
|
|
|
282
327
|
// Return top 10 with diversity caps:
|
|
283
328
|
// - Max 2 per category (prevents single-category saturation)
|
|
284
329
|
// - Max 1 per standard title (prevents same-file rule pairs wasting slots)
|
|
330
|
+
// Predicted standards are included first (they skip scoring)
|
|
285
331
|
const MAX_PER_CATEGORY = 2;
|
|
286
332
|
const MAX_PER_TITLE = 1;
|
|
287
333
|
const top = [];
|
|
288
334
|
const categoryCounts = {};
|
|
289
335
|
const titleCounts = {};
|
|
290
|
-
|
|
336
|
+
const seenPredicted = new Set();
|
|
337
|
+
|
|
338
|
+
// Add predicted standards first — they have proven relevance
|
|
339
|
+
for (const standard of predictedStandards) {
|
|
291
340
|
const cat = standard.category;
|
|
292
341
|
const title = standard.title || standard.element;
|
|
342
|
+
const key = standard.element;
|
|
293
343
|
categoryCounts[cat] = (categoryCounts[cat] || 0) + 1;
|
|
294
344
|
titleCounts[title] = (titleCounts[title] || 0) + 1;
|
|
345
|
+
seenPredicted.add(key);
|
|
295
346
|
if (categoryCounts[cat] <= MAX_PER_CATEGORY && titleCounts[title] <= MAX_PER_TITLE) {
|
|
296
347
|
top.push(standard);
|
|
297
348
|
if (top.length >= 10) break;
|
|
298
349
|
}
|
|
299
350
|
}
|
|
300
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
|
+
|
|
301
397
|
return createSuccessResponse({
|
|
302
398
|
standards: top,
|
|
303
399
|
categories,
|
|
304
|
-
total_matched: result.rows.length
|
|
400
|
+
total_matched: result.rows.length,
|
|
401
|
+
frame_id: frameId
|
|
305
402
|
}, 'Relevant standards retrieved');
|
|
306
403
|
}
|
|
307
404
|
|