roadmapsmith 0.9.11 → 0.9.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roadmapsmith",
3
- "version": "0.9.11",
3
+ "version": "0.9.12",
4
4
  "description": "Evidence-backed ROADMAP.md generator and sync tool for AI coding agents.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -50,26 +50,23 @@ const GENERIC_TASK_TOKENS = new Set([
50
50
  'phrases', 'conceptual',
51
51
  ]);
52
52
 
53
- // Verbs that indicate the task describes work still to be done, not completed work.
54
- // When a task starts with one of these verbs, code token overlap alone cannot pass it —
55
- // either an Evidence line (authoritativeEvidence.passed) or test evidence is required.
56
- const ACTION_VERBS = new Set([
57
- 'agregar', 'mostrar', 'implementar', 'configurar', 'reemplazar', 'cambiar',
58
- 'corregir', 'manejar', 'proteger', 'sanitizar', 'validar', 'deshabilitar',
59
- 'generar', 'expandir', 'reducir', 'completar', 'crear', 'eliminar',
60
- 'add', 'show', 'implement', 'configure', 'replace', 'change', 'fix',
61
- 'handle', 'protect', 'sanitize', 'validate', 'disable', 'generate',
62
- 'expand', 'reduce', 'complete', 'create', 'remove'
63
- ]);
53
+ // Patterns that indicate the task describes work still to be done, not completed work.
54
+ // Regex form catches verb and noun forms ("Manejo") and two-word constructions ("Recovery path")
55
+ // that an exact-match Set would miss. When a task matches, code token overlap alone cannot pass
56
+ // it either an Evidence line or high-confidence evidence (code + test) is required.
57
+ const CHANGE_VERB_PATTERNS = [
58
+ // Spanish verb and noun forms of pending-work descriptions
59
+ /^(agregar|añadir|implementar|configurar|reemplazar|cambiar|corregir|manejar|manejo|proteger|sanitizar|validar|deshabilitar|mostrar|generar|expandir|reducir|completar|crear|eliminar|migrar|refactorizar|recovery\s+path)\b/i,
60
+ // English
61
+ /^(add|implement|configure|replace|change|fix|handle|protect|sanitize|validate|disable|show|generate|expand|reduce|complete|create|remove|migrate|refactor|recovery\s+path)\b/i,
62
+ ];
64
63
 
65
- function taskStartsWithActionVerb(taskText) {
64
+ function taskDescribesChange(taskText) {
66
65
  const normalized = String(taskText)
67
- .replace(/\*\*([^*]+)\*\*/g, '$1')
68
- .replace(/\[([^\]]+)\]/g, '$1')
69
- .trim()
70
- .toLowerCase();
71
- const firstWord = normalized.split(/[\s,;:()[\]]+/)[0] || '';
72
- return ACTION_VERBS.has(firstWord);
66
+ .replace(/^\*\*\[.*?\]\*\*\s*/, '')
67
+ .replace(/^\[.*?\]\s*/, '')
68
+ .trim();
69
+ return CHANGE_VERB_PATTERNS.some((p) => p.test(normalized));
73
70
  }
74
71
 
75
72
  const CANONICAL_FILES = {
@@ -201,6 +198,7 @@ function hasFileExtension(token) {
201
198
 
202
199
  function isLikelyPath(token) {
203
200
  if (token.includes('*') || token.includes('?')) return false; // glob/wildcard
201
+ if (/^\/api\//i.test(token)) return false; // HTTP API route paths are not file paths
204
202
  if (/^\.{1,2}\/|^\//.test(token)) {
205
203
  // Bare "/" or "./" with nothing after is not a real path (e.g. "API / ESC-POS" → "/")
206
204
  return /[A-Za-z0-9_]/.test(token);
@@ -1195,9 +1193,11 @@ function validateTask(task, context, config, plugins) {
1195
1193
  let confidence = 'low';
1196
1194
  if (authoritativeEvidence.passed) {
1197
1195
  confidence = authoritativeEvidence.confidence || 'medium';
1198
- } else if (meetsStrongThreshold) {
1196
+ } else if (meetsStrongThreshold && evidence.test) {
1197
+ // 'high' requires code + test — code + feature-surface alone is 'medium'
1198
+ // so that action-verb tasks (path hint exists but no test) stay gated.
1199
1199
  confidence = 'high';
1200
- } else if (strongEvidenceCount === 1 || hasDirectReferencePass || hasArtifactTaskPass || hasTrustedRuleEvidencePass) {
1200
+ } else if (meetsStrongThreshold || strongEvidenceCount === 1 || hasDirectReferencePass || hasArtifactTaskPass || hasTrustedRuleEvidencePass) {
1201
1201
  confidence = 'medium';
1202
1202
  }
1203
1203
 
@@ -1241,24 +1241,22 @@ function validateTask(task, context, config, plugins) {
1241
1241
  passed = false;
1242
1242
  }
1243
1243
 
1244
- // Action-verb gate (Causa 3): unchecked tasks whose text starts with a pending-work verb
1245
- // (Agregar, Configurar, Add, Fix, …) cannot pass on code token overlap alone.
1246
- // The verb signals "this is work to do", so the validator requires either:
1247
- // a) an Evidence line confirming the work is done (authoritativeEvidence.passed), or
1248
- // b) test evidence proving the feature is exercised, or
1249
- // c) grant-evidence from a config rule (hasTrustedRuleEvidencePass), or
1250
- // d) canonical artifact evidence (hasArtifactTaskPass — e.g. "Add SECURITY.md").
1244
+ // Action-verb gate (Causa 3): unchecked tasks that describe a change to be made
1245
+ // (Agregar, Configurar, Add, Fix, Manejo, Recovery path, …) cannot pass on code token overlap alone.
1246
+ // Requires either: an Evidence line (authoritativeEvidence.passed), high-confidence evidence
1247
+ // (code + test), grant-evidence from config (hasTrustedRuleEvidencePass), or canonical artifact
1248
+ // evidence (hasArtifactTaskPass e.g. "Add SECURITY.md").
1251
1249
  if (
1252
1250
  !task.checked &&
1253
1251
  passed &&
1254
- taskStartsWithActionVerb(task.text) &&
1252
+ taskDescribesChange(task.text) &&
1255
1253
  !authoritativeEvidence.passed &&
1256
1254
  !hasTrustedRuleEvidencePass &&
1257
1255
  !hasArtifactTaskPass &&
1258
- !evidence.test
1256
+ confidence !== 'high'
1259
1257
  ) {
1260
1258
  passed = false;
1261
- const actionVerbReason = 'action-verb task requires high-confidence evidence or Evidence line';
1259
+ const actionVerbReason = 'action task requires Evidence line or high-confidence evidence (code + test) to be marked complete';
1262
1260
  if (!uniqueReasons.includes(actionVerbReason)) {
1263
1261
  uniqueReasons.push(actionVerbReason);
1264
1262
  }