Publish rewritten technical article archive
Build and deploy / deploy (push) Failing after 15s

This commit is contained in:
2026-08-02 22:19:34 +03:00
parent 7c5b19c960
commit 2d914b543f
375 changed files with 2513 additions and 4 deletions
+58
View File
@@ -0,0 +1,58 @@
import articles from '../data/articles.json' with { type: 'json' };
import { agentRewriteRevisions } from '../data/agent-rewrite-revisions.mjs';
const archiveBySlug = new Map(articles.map((article) => [article.slug, article]));
const forbidden = [
/synthetic-plan-hand-off/i,
/productionEffect/i,
/future-only/i,
/plan\/scenario/i,
/source cutoff/i,
/editorial date/i,
/planDate/i,
/not-collected/i,
/not-attempted/i,
/future owner/i,
/author trajectory/i,
/развитие автора/i,
/План на \d{4}/i,
];
function bodyWithoutSources(content) {
return content
.replace(/<h2>Проверяемые источники<\/h2>[\s\S]*$/, '')
.replace(/<[^>]+>/g, ' ')
.replace(/&(?:quot|amp|lt|gt|#039);/g, ' ')
.replace(/\s+/g, ' ')
.trim();
}
const failures = [];
const seen = new Set();
for (const revision of agentRewriteRevisions) {
const body = bodyWithoutSources(revision.contentHtml || '');
const issues = [];
if (!archiveBySlug.has(revision.slug)) issues.push('slug отсутствует в архиве');
if (seen.has(revision.slug)) issues.push('дубликат slug');
seen.add(revision.slug);
if (!revision.title || !revision.excerpt) issues.push('нет title/excerpt');
if (body.length < 5000 || body.length > 15000) issues.push('основной текст вне 5 000–15 000 знаков');
if (!/<figure>[\s\S]*?<img\b[^>]+alt=(?:"[^"<>]+"|'[^'<>]+')[\s\S]*?<\/figure>/.test(revision.contentHtml)) issues.push('нет figure с alt');
if (!/<table>[\s\S]*?<\/table>/.test(revision.contentHtml)) issues.push('нет table');
if (!/<pre><code>[\s\S]*?<\/code><\/pre>/.test(revision.contentHtml)) issues.push('нет pre/code');
if (!/<ol>[\s\S]*?<\/ol>/.test(revision.contentHtml)) issues.push('нет ol');
if (!/<h2>Проверяемые источники<\/h2>[\s\S]*?https?:\/\//.test(revision.contentHtml)) issues.push('нет проверяемого источника');
const meta = forbidden.find((pattern) => pattern.test(body));
if (meta) issues.push('мета-лексика: ' + meta);
if (issues.length) failures.push({ index: revision.index, slug: revision.slug, issues });
}
const report = {
drafts: agentRewriteRevisions.length,
uniqueSlugs: seen.size,
failures,
nextExpectedIndex: articles.findIndex((article) => !seen.has(article.slug)) + 1 || null,
};
console.log(JSON.stringify(report, null, 2));
if (failures.length) process.exitCode = 1;
+4 -4
View File
@@ -82,7 +82,7 @@ for (const slug of slugs) {
const content = cleanReaderHtml(article.contentHtml);
const body = bodyText(content);
const imageSources = [...content.matchAll(/<img[^>]+src="([^"]+)"/g)].map((match) => match[1]);
const imageSources = [...content.matchAll(/<img[^>]+\bsrc=(['"])(.*?)\1/gi)].map((match) => match[2]);
const figures = [...content.matchAll(/<figure>([\s\S]*?)<\/figure>/g)].map((match) => match[1]);
if (article.readingMinutes < 8) issues.push('указано меньше 8 минут чтения');
@@ -98,7 +98,7 @@ for (const slug of slugs) {
if (count(content, /<table>/g) < 1 || count(content, /<thead>/g) < 1) issues.push('нет доступной таблицы');
if (count(content, /<pre><code>/g) < 1) issues.push('нет воспроизводимого примера');
if (count(content, /<ol>/g) < 1) issues.push('нет последовательности проверки или действий');
if (count(content, /<a href="https?:\/\//g) < 2) issues.push('меньше двух внешних источников');
if (count(content, /<a\b[^>]*\bhref=(['"])https?:\/\/[^'"]+\1/gi) < 2) issues.push('меньше двух внешних источников');
if (!content.includes('<h2>Проверяемые источники</h2>')) issues.push('нет отдельного раздела с источниками');
const proseWithoutCode = content
.replace(/<pre><code>[\s\S]*?<\/code><\/pre>/g, '')
@@ -114,8 +114,8 @@ for (const slug of slugs) {
for (const figure of figures) {
const image = figure.match(/<img\b[^>]*>/);
const alt = image?.[0].match(/\balt="([^"]*)"/);
if (!image || !alt || alt[1].trim() === '') {
const alt = image?.[0].match(/\balt=(['"])(.*?)\1/i);
if (!image || !alt || alt[2].trim() === '') {
issues.push('у рисунка нет содержательного alt-текста');
break;
}