Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 2x 1x 1x 5x 5x 1x 2x 2x 10x 10x 2x 2x | /**
* @module scripts/validators/article/rules/placeholders
* @description Placeholder-pattern scanner — detects template tokens
* that the AI agent must replace during Pass-1 / Pass-2.
*
* Rule census: extracted from
* `scripts/validate-article.ts` lines 67–73
* (`PLACEHOLDER_PATTERNS`). Logic is byte-identical to the
* original.
*
* @author Hack23 AB
* @license Apache-2.0
*/
/**
* Placeholder strings that templates contain on disk and that the AI
* agent is instructed to replace during Pass-1 / Pass-2. Any of these
* surviving in the aggregated article means the agent skipped the
* substitution and the article is not publishable.
*
* Keep this list in sync with the markers used in
* `analysis/templates/*.md`.
*/
export const PLACEHOLDER_PATTERNS: readonly RegExp[] = [
/\[REQUIRED:[^\]]*\]/,
/AI[_-]MUST[_-]REPLACE/i,
/\bTBD\b\s*:/,
/<insert\b[^>]*>/i,
/\bFILL[_\s]IN\b/i,
];
/** Return every placeholder match in `text`. Returns the matched string for each pattern hit. */
export function scanPlaceholders(text: string): string[] {
const hits: string[] = [];
for (const pat of PLACEHOLDER_PATTERNS) {
const m = text.match(pat);
if (m) hits.push(m[0]);
}
return hits;
}
import type { ArticleViolation } from '../types.js';
/** Emit one `unresolved-placeholder` violation per matched pattern. */
export function checkPlaceholders(rel: string, text: string): ArticleViolation[] {
const out: ArticleViolation[] = [];
for (const pat of PLACEHOLDER_PATTERNS) {
const m = text.match(pat);
if (m) {
out.push({
file: rel,
code: 'unresolved-placeholder',
message: `Template placeholder ${JSON.stringify(m[0])} was not replaced during AI-FIRST Pass-2 — see analysis/templates/README.md "Reader-Facing Output Contract".`,
});
}
}
return out;
}
|