@forwardimpact/libwiki 0.2.21 → 0.2.22
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/audit/rules.js +17 -11
package/package.json
CHANGED
package/src/audit/rules.js
CHANGED
|
@@ -75,16 +75,22 @@ const columnCount = (expected) => (s) =>
|
|
|
75
75
|
const exists = (s) => (s.exists ? null : {});
|
|
76
76
|
const expired = (s, ctx) => (s.expires_at < ctx.today ? {} : null);
|
|
77
77
|
|
|
78
|
+
// The heading must equal `requiredLine` exactly — a suffixed variant like
|
|
79
|
+
// "### Decision — <summary>" does not satisfy it, but is reported as a
|
|
80
|
+
// near miss so the writer fixes the heading instead of hunting for a
|
|
81
|
+
// "missing" line that is right there.
|
|
78
82
|
function entryHasDecision(lines, startIdx, requiredLine, stopRe) {
|
|
79
83
|
let seen = 0;
|
|
84
|
+
let nearMiss = null;
|
|
80
85
|
for (let j = startIdx + 1; j < lines.length && seen < 5; j++) {
|
|
81
86
|
const ln = lines[j].trim();
|
|
82
87
|
if (ln === "") continue;
|
|
83
88
|
seen++;
|
|
84
|
-
if (ln === requiredLine) return true;
|
|
85
|
-
if (
|
|
89
|
+
if (ln === requiredLine) return { found: true };
|
|
90
|
+
if (nearMiss === null && ln.startsWith(requiredLine)) nearMiss = ln;
|
|
91
|
+
if (stopRe.test(lines[j])) break;
|
|
86
92
|
}
|
|
87
|
-
return false;
|
|
93
|
+
return { found: false, nearMiss };
|
|
88
94
|
}
|
|
89
95
|
|
|
90
96
|
const decisionWithin5 =
|
|
@@ -92,12 +98,9 @@ const decisionWithin5 =
|
|
|
92
98
|
(s) => {
|
|
93
99
|
const offenders = [];
|
|
94
100
|
for (let i = 0; i < s.fileLines.length; i++) {
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
) {
|
|
99
|
-
offenders.push({ lineNo: i + 1 });
|
|
100
|
-
}
|
|
101
|
+
if (!entryRe.test(s.fileLines[i])) continue;
|
|
102
|
+
const res = entryHasDecision(s.fileLines, i, requiredLine, stopRe);
|
|
103
|
+
if (!res.found) offenders.push({ lineNo: i + 1, nearMiss: res.nearMiss });
|
|
101
104
|
}
|
|
102
105
|
return offenders.length === 0 ? null : offenders;
|
|
103
106
|
};
|
|
@@ -283,8 +286,11 @@ export const RULES = [
|
|
|
283
286
|
requiredLine: DECISION_HEADING,
|
|
284
287
|
stopRe: /^##\s/,
|
|
285
288
|
}),
|
|
286
|
-
message: () =>
|
|
287
|
-
|
|
289
|
+
message: (_s, r) =>
|
|
290
|
+
r.nearMiss
|
|
291
|
+
? `Entry opens with '${r.nearMiss}'; the heading must be exactly '${DECISION_HEADING}' — move the suffix into the body`
|
|
292
|
+
: `Entry lacks a line that is exactly '${DECISION_HEADING}'`,
|
|
293
|
+
hint: `open each '## YYYY-MM-DD' entry with a line containing exactly '${DECISION_HEADING}' (no suffix — the check is an exact match); put the one-line summary in the body below it, drawn from the entry's own narrative — do not invent rationale the entry does not support`,
|
|
288
294
|
},
|
|
289
295
|
|
|
290
296
|
// -- Weekly logs (sealed parts) --
|