roadmapsmith 0.6.0 → 0.7.0

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.
@@ -6,6 +6,8 @@ const { walkFiles, detectTestFrameworks } = require('../io');
6
6
  const { collectPluginContributions } = require('../config');
7
7
  const { escapeRegExp, tokenize } = require('../utils');
8
8
 
9
+ const CONFIDENCE_RANK = { low: 0, medium: 1, high: 2 };
10
+
9
11
  const CODE_EXTENSIONS = new Set([
10
12
  '.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx', '.py', '.go', '.rs', '.java', '.kt', '.swift', '.rb', '.php', '.cs'
11
13
  ]);
@@ -29,6 +31,13 @@ const GENERIC_TASK_TOKENS = new Set([
29
31
  'tests'
30
32
  ]);
31
33
 
34
+ const CANONICAL_FILES = {
35
+ security: 'SECURITY.md',
36
+ readme: 'README.md',
37
+ changelog: 'CHANGELOG.md',
38
+ license: 'LICENSE'
39
+ };
40
+
32
41
  function readFileIndex(projectRoot, files) {
33
42
  const index = [];
34
43
  for (const relativePath of files) {
@@ -213,10 +222,23 @@ function findTestEvidence(taskText, fileIndex) {
213
222
 
214
223
  function findArtifactEvidence(taskText, fileIndex) {
215
224
  const normalized = String(taskText).toLowerCase();
216
- const matches = [];
225
+ const files = [];
226
+ const heuristicArtifacts = [];
227
+
228
+ for (const [keyword, filename] of Object.entries(CANONICAL_FILES)) {
229
+ if (normalized.includes(keyword)) {
230
+ const hit = fileIndex.find(
231
+ (f) => f.relativePath === filename || f.relativePath.endsWith('/' + filename)
232
+ );
233
+ if (hit) {
234
+ files.push(hit.relativePath);
235
+ heuristicArtifacts.push(hit.relativePath);
236
+ }
237
+ }
238
+ }
217
239
 
218
240
  if (!isDocTask(taskText) && !normalized.includes('artifact') && !normalized.includes('release')) {
219
- return matches;
241
+ return { files, heuristicArtifacts };
220
242
  }
221
243
 
222
244
  const artifactPatterns = [
@@ -229,12 +251,12 @@ function findArtifactEvidence(taskText, fileIndex) {
229
251
  ];
230
252
 
231
253
  for (const file of fileIndex) {
232
- if (artifactPatterns.some((pattern) => pattern.test(file.relativePath))) {
233
- matches.push(file.relativePath);
254
+ if (artifactPatterns.some((pattern) => pattern.test(file.relativePath)) && !files.includes(file.relativePath)) {
255
+ files.push(file.relativePath);
234
256
  }
235
257
  }
236
258
 
237
- return matches.slice(0, 20);
259
+ return { files: files.slice(0, 20), heuristicArtifacts };
238
260
  }
239
261
 
240
262
  function evaluateRule(rule, task, context) {
@@ -326,7 +348,7 @@ function validateTask(task, context, config, plugins) {
326
348
  const filesFromSymbols = findFilesBySymbols(symbolHints, context.fileIndex);
327
349
  const filesFromCode = findCodeEvidence(task.text, context.fileIndex);
328
350
  const filesFromTests = findTestEvidence(task.text, context.fileIndex);
329
- const filesFromArtifacts = findArtifactEvidence(task.text, context.fileIndex);
351
+ const { files: filesFromArtifacts, heuristicArtifacts } = findArtifactEvidence(task.text, context.fileIndex);
330
352
 
331
353
  const evidence = {
332
354
  code: filesFromCode.length > 0 || filesFromSymbols.length > 0,
@@ -336,7 +358,8 @@ function validateTask(task, context, config, plugins) {
336
358
  symbols: filesFromSymbols,
337
359
  codeFiles: filesFromCode,
338
360
  testFiles: filesFromTests,
339
- artifactFiles: filesFromArtifacts
361
+ artifactFiles: filesFromArtifacts,
362
+ heuristicArtifacts
340
363
  };
341
364
 
342
365
  const reasons = [];
@@ -370,7 +393,7 @@ function validateTask(task, context, config, plugins) {
370
393
  const attempted = hasEvidence || pathHints.length > 0 || symbolHints.length > 0;
371
394
 
372
395
  const evidenceCount = [evidence.code, evidence.test, evidence.artifact].filter(Boolean).length;
373
- const confidence = evidenceCount >= 2 ? 'high' : evidenceCount === 1 ? 'medium' : attempted ? 'medium' : 'low';
396
+ const confidence = evidenceCount >= 2 ? 'high' : evidenceCount === 1 ? 'medium' : 'low';
374
397
 
375
398
  return {
376
399
  taskId: task.id,
@@ -417,9 +440,25 @@ function auditValidation(tasks, results) {
417
440
  };
418
441
  }
419
442
 
443
+ function applyMinimumConfidence(results, minimumConfidence) {
444
+ const minRank = CONFIDENCE_RANK[minimumConfidence] ?? 0;
445
+ if (minRank === 0) return;
446
+ for (const result of Object.values(results)) {
447
+ if ((CONFIDENCE_RANK[result.confidence] ?? 0) < minRank) {
448
+ result.passed = false;
449
+ result.reasons = [
450
+ ...result.reasons,
451
+ `validation confidence "${result.confidence}" is below required "${minimumConfidence}"`
452
+ ];
453
+ }
454
+ }
455
+ }
456
+
420
457
  module.exports = {
421
458
  auditValidation,
422
459
  buildValidationContext,
423
460
  validateTask,
424
- validateTasks
461
+ validateTasks,
462
+ CONFIDENCE_RANK,
463
+ applyMinimumConfidence
425
464
  };