roadmapsmith 0.9.9 → 0.9.10
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 +1 -1
- package/src/validator/index.js +45 -16
package/package.json
CHANGED
package/src/validator/index.js
CHANGED
|
@@ -178,7 +178,11 @@ function hasFileExtension(token) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
function isLikelyPath(token) {
|
|
181
|
-
if (
|
|
181
|
+
if (token.includes('*') || token.includes('?')) return false; // glob/wildcard
|
|
182
|
+
if (/^\.{1,2}\/|^\//.test(token)) {
|
|
183
|
+
// Bare "/" or "./" with nothing after is not a real path (e.g. "API / ESC-POS" → "/")
|
|
184
|
+
return /[A-Za-z0-9_]/.test(token);
|
|
185
|
+
}
|
|
182
186
|
if (hasFileExtension(token)) return true;
|
|
183
187
|
if (KNOWN_PATH_ROOTS.some((root) => token.startsWith(root))) return true;
|
|
184
188
|
// The ">= 2 slashes" rule was intentionally removed: it caused conceptual slash phrases
|
|
@@ -262,14 +266,16 @@ function extractExplicitPaths(text) {
|
|
|
262
266
|
for (const token of quoted) {
|
|
263
267
|
const clean = token.slice(1, -1);
|
|
264
268
|
if (clean.includes('*') || clean.includes('?')) continue; // glob
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
269
|
+
const hasSlash = clean.includes('/') || clean.includes('\\');
|
|
270
|
+
// Require a slash or a known file extension — rejects property access like err.message,
|
|
271
|
+
// fs.readFileSync, error.stack (whose extensions are not in KNOWN_FILE_EXTENSIONS).
|
|
272
|
+
if (!hasSlash && !hasKnownFileExtension(clean)) continue;
|
|
273
|
+
const lineMatch = LINE_REF_RE.exec(clean);
|
|
274
|
+
if (lineMatch && hasKnownFileExtension(lineMatch[1])) {
|
|
275
|
+
lineReferenceHints.add(lineMatch[1]);
|
|
276
|
+
results.add(lineMatch[1]);
|
|
277
|
+
} else {
|
|
278
|
+
results.add(clean);
|
|
273
279
|
}
|
|
274
280
|
}
|
|
275
281
|
|
|
@@ -288,7 +294,9 @@ function extractExplicitPaths(text) {
|
|
|
288
294
|
}
|
|
289
295
|
}
|
|
290
296
|
|
|
291
|
-
const paths = Array.from(results)
|
|
297
|
+
const paths = Array.from(results)
|
|
298
|
+
.filter((p) => !p.includes('*') && !p.includes('?'))
|
|
299
|
+
.sort((left, right) => left.localeCompare(right));
|
|
292
300
|
return { paths, lineReferenceHints };
|
|
293
301
|
}
|
|
294
302
|
|
|
@@ -1060,7 +1068,9 @@ function validateTask(task, context, config, plugins) {
|
|
|
1060
1068
|
code: filesFromCode.length > 0 || filesFromSymbols.length > 0,
|
|
1061
1069
|
test: filesFromTests.length > 0,
|
|
1062
1070
|
artifact: filesFromArtifacts.length > 0,
|
|
1063
|
-
|
|
1071
|
+
// Use only pure path hints (not line-reference hints) so that "file.ts:169" style hints
|
|
1072
|
+
// — which indicate WHERE to implement — do not contribute to feature-surface scoring.
|
|
1073
|
+
files: filesFromPurePathHints,
|
|
1064
1074
|
symbols: filesFromSymbols,
|
|
1065
1075
|
codeFiles: filesFromCode,
|
|
1066
1076
|
weakPathFiles: filesFromWeakPathTokens,
|
|
@@ -1077,7 +1087,9 @@ function validateTask(task, context, config, plugins) {
|
|
|
1077
1087
|
applyAuthoritativeEvidence(evidence, authoritativeEvidence, context.fileIndex);
|
|
1078
1088
|
|
|
1079
1089
|
const reasons = [];
|
|
1080
|
-
|
|
1090
|
+
// Suppress path hint failures when authoritative evidence already confirms the task —
|
|
1091
|
+
// a bad path hint (typo, moved file) should not override a passing Evidence line.
|
|
1092
|
+
if (pathHints.length > 0 && filesFromPaths.length === 0 && !authoritativeEvidence.passed) {
|
|
1081
1093
|
reasons.push(`missing referenced file(s): ${pathHints.join(', ')}`);
|
|
1082
1094
|
}
|
|
1083
1095
|
if (Array.isArray(authoritativeEvidence.reasons) && authoritativeEvidence.reasons.length > 0) {
|
|
@@ -1182,7 +1194,19 @@ function validateTask(task, context, config, plugins) {
|
|
|
1182
1194
|
uniqueReasons = Array.from(new Set(uniqueReasons));
|
|
1183
1195
|
}
|
|
1184
1196
|
|
|
1185
|
-
|
|
1197
|
+
// hasDirectReferencePass is intentionally excluded: a path hint in task text indicates
|
|
1198
|
+
// WHERE to implement, not that implementation is done. Unchecked tasks need authoritative
|
|
1199
|
+
// evidence, artifact evidence, or strong code+test threshold to pass.
|
|
1200
|
+
// Already-checked tasks with found path hints are preserved via shouldPreserveCheckedTask.
|
|
1201
|
+
let passed = authoritativeEvidence.passed || hasArtifactTaskPass || hasTrustedRuleEvidencePass || meetsStrongThreshold;
|
|
1202
|
+
|
|
1203
|
+
if (!passed && !task.checked && hasDirectReferencePass) {
|
|
1204
|
+
const locationReason = 'file reference shows implementation location, not confirmed completion';
|
|
1205
|
+
if (!uniqueReasons.includes(locationReason)) {
|
|
1206
|
+
uniqueReasons.push(locationReason);
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1186
1210
|
if (task.warningText && passed && !authoritativeEvidence.passed && !meetsStrongThreshold) {
|
|
1187
1211
|
passed = false;
|
|
1188
1212
|
uniqueReasons.push(task.warningText);
|
|
@@ -1192,15 +1216,20 @@ function validateTask(task, context, config, plugins) {
|
|
|
1192
1216
|
passed = false;
|
|
1193
1217
|
}
|
|
1194
1218
|
|
|
1219
|
+
// Preserve already-checked tasks when the validator can't confirm implementation but also
|
|
1220
|
+
// can't find strong evidence against it. Two cases:
|
|
1221
|
+
// 1. No path/symbol hints at all — no machine-readable claims to evaluate.
|
|
1222
|
+
// 2. Path hints resolve to existing files but code/test evidence is absent —
|
|
1223
|
+
// file presence is not implementation; don't uncheck on that alone.
|
|
1224
|
+
// Symbol hints are NOT preserved: a missing symbol is a concrete falsifiable claim.
|
|
1195
1225
|
const shouldPreserveCheckedTask =
|
|
1196
1226
|
task.checked &&
|
|
1197
1227
|
!passed &&
|
|
1198
1228
|
!authoritativeEvidence.active &&
|
|
1199
|
-
purePathHints.length === 0 &&
|
|
1200
1229
|
symbolHints.length === 0 &&
|
|
1201
|
-
|
|
1230
|
+
negativeSignalMatches.length === 0 &&
|
|
1202
1231
|
evidence.structuralEvidence !== false &&
|
|
1203
|
-
|
|
1232
|
+
(hasDirectReferencePass || purePathHints.length === 0);
|
|
1204
1233
|
let preservedCheckedState = false;
|
|
1205
1234
|
if (shouldPreserveCheckedTask) {
|
|
1206
1235
|
passed = true;
|