@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.
@@ -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) => b.relevance_score - a.relevance_score);
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 no free tier access
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 from all relevant categories (static + recency)
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 = 'validated' THEN 2
260
- ELSE 3
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
- for (const standard of ranked) {
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