test: repoint verification at Astro output
Verify rendered routes, translations, CSS dependencies, variables, and built CSS values. Keep the original assertion count; do not alter site components or fixtures.
This commit is contained in:
+49
-18
@@ -1,4 +1,5 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8');
|
||||
const pages = [
|
||||
@@ -13,27 +14,57 @@ const pages = [
|
||||
'hands-on/starter/index.html',
|
||||
'hands-on/rules/index.html',
|
||||
];
|
||||
const walk = (path) =>
|
||||
readdirSync(path).flatMap((name) => {
|
||||
const child = join(path, name);
|
||||
return statSync(child).isDirectory() ? walk(child) : [child];
|
||||
});
|
||||
const definitions = (css) => new Set([...css.matchAll(/(--[\w-]+)\s*:/g)].map(([, name]) => name));
|
||||
const uses = (css) => new Set([...css.matchAll(/var\((--[\w-]+)/g)].map(([, name]) => name));
|
||||
const builtCss = walk('dist/_astro')
|
||||
.filter((path) => path.endsWith('.css'))
|
||||
.map((path) => readFileSync(path, 'utf8'))
|
||||
.join('\n');
|
||||
const builtValues = {
|
||||
colors: new Set(builtCss.match(/#[0-9a-fA-F]{3,8}\b|(?:rgba?|hsla?)\([^)]*\)/g) || []),
|
||||
sizes: new Set(builtCss.match(/\b\d+(?:\.\d+)?(?:px|rem|em|vw|vh)\b/g) || []),
|
||||
};
|
||||
// Captured from origin/main after its Astro build. This catches a value that is
|
||||
// silently changed to a nearby token: source token checks cannot see that drift.
|
||||
const baselineValues = JSON.parse(read('.agents/snapshots/built-css-values.json'));
|
||||
|
||||
for (const page of pages) {
|
||||
const html = read(page);
|
||||
if (!html.includes('name="viewport"')) throw new Error(`${page} lacks a viewport declaration`);
|
||||
if (html.match(/<(script|link)[^>]+(src|href)="https?:[^\"]+"/i))
|
||||
throw new Error(`${page} has an external runtime dependency`);
|
||||
}
|
||||
const shared = read('chapters.css');
|
||||
const skills = read('skills/styles.css');
|
||||
const guide = read('full-guide/index.html');
|
||||
const guideAudit = read('full-guide/audit.css');
|
||||
for (const token of ['@media(max-width:800px)', '@media(max-width:520px)'])
|
||||
if (!shared.includes(token)) throw new Error(`shared chapter CSS lacks ${token}`);
|
||||
for (const token of [
|
||||
'minmax(0,1.3fr)',
|
||||
'overflow-wrap:anywhere',
|
||||
'@media(max-width:800px)',
|
||||
'prefers-reduced-motion',
|
||||
])
|
||||
if (!skills.includes(token)) throw new Error(`skills package CSS lacks ${token}`);
|
||||
if (!guide.includes('href="audit.css"'))
|
||||
throw new Error('full guide does not load its responsive audit overrides');
|
||||
for (const token of ['.handoff table', '.route-table button', 'overflow-wrap:anywhere'])
|
||||
if (!guideAudit.includes(token)) throw new Error(`full guide audit CSS lacks ${token}`);
|
||||
for (const file of walk('dist').filter((path) => path.endsWith('.css'))) {
|
||||
const css = readFileSync(file, 'utf8').replace(/\/\*[\s\S]*?\*\//g, '');
|
||||
if (/@import|src:\s*url\(['"]?https?:|url\(['"]?https?:/i.test(css))
|
||||
throw new Error(`${file} has an external CSS dependency`);
|
||||
}
|
||||
for (const [kind, values] of Object.entries(baselineValues)) {
|
||||
const missing = values.filter((value) => !builtValues[kind].has(value));
|
||||
if (missing.length) throw new Error(`built CSS lost ${kind}: ${missing.join(', ')}`);
|
||||
}
|
||||
// Legacy fixtures still ship directly. Resolve every var() from the stylesheets
|
||||
// that page actually links; Astro's tokens cannot mask a broken standalone page.
|
||||
for (const page of pages) {
|
||||
const html = read(page);
|
||||
const linked = [...html.matchAll(/<link[^>]+rel="stylesheet"[^>]+href="([^"]+)"/gi)].map(
|
||||
([, href]) => href,
|
||||
);
|
||||
const styles = linked
|
||||
.filter((href) => !/^https?:/i.test(href))
|
||||
.map((href) => read(join(dirname(page), href)));
|
||||
const defined = new Set(styles.flatMap((css) => [...definitions(css)]));
|
||||
const unresolved = new Set(
|
||||
styles.flatMap((css) =>
|
||||
[...uses(css)].filter((name) => name !== '--score' && !defined.has(name)),
|
||||
),
|
||||
);
|
||||
if (unresolved.size)
|
||||
throw new Error(`${page} has unresolved CSS variables: ${[...unresolved].join(', ')}`);
|
||||
}
|
||||
console.log('responsive UI audit passed');
|
||||
|
||||
+253
-387
@@ -1,397 +1,263 @@
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
const read = (path) => readFileSync(new URL(`../${path}`, import.meta.url), 'utf8');
|
||||
const landingHtml = read('index.html');
|
||||
const html = read('full-guide/index.html');
|
||||
const js = read('app.js');
|
||||
const refs = read('docs/references/README.md');
|
||||
const additional = read('docs/references/additional-reading.md');
|
||||
const starterHtml = read('hands-on/starter/index.html');
|
||||
const starterJs = read('hands-on/starter/app.js');
|
||||
const skillSources = read('docs/references/skill-sources.md');
|
||||
const modelRouting = read('docs/references/model-routing.md');
|
||||
const rulesHtml = read('rules/index.html');
|
||||
const rulesJs = read('rules/app.js');
|
||||
const rulesCss = read('rules/styles.css');
|
||||
const reviewHtml = read('skills-review/index.html');
|
||||
const reviewJs = read('skills-review/app.js');
|
||||
const reviewFiles = read('skills-review/files.js');
|
||||
const reviewLensCss = read('skills-review/change-lens.css');
|
||||
const chaptersCss = read('chapters.css');
|
||||
const summaryHtml = read('summary/index.html');
|
||||
const modelsHtml = read('models/index.html');
|
||||
const agentsHtml = read('agents/index.html');
|
||||
const skillsHtml = read('skills/index.html');
|
||||
const reviewCatalog = read('skills-review/catalog.js');
|
||||
const reviewVoteJs = read('skills-review/vote.js');
|
||||
const voteService = read('vote-service/main.go');
|
||||
for (const url of [
|
||||
'https://code.claude.com/docs/en/sub-agents',
|
||||
'https://code.claude.com/docs/en/skills',
|
||||
'https://code.claude.com/docs/en/worktrees',
|
||||
'https://git-scm.com/docs/git-worktree.html',
|
||||
'https://developers.openai.com/codex/skills',
|
||||
])
|
||||
if (!refs.includes(url)) throw new Error(`missing reference ${url}`);
|
||||
console.log('content verification passed');
|
||||
for (const token of [
|
||||
'data-phase="plan"',
|
||||
'data-phase="build"',
|
||||
'data-phase="review"',
|
||||
'data-tree="main"',
|
||||
'data-tree="ui"',
|
||||
'data-worker="ui"',
|
||||
'data-route="plan"',
|
||||
'data-model-provider="openai"',
|
||||
'data-model-provider="claude"',
|
||||
'data-model-provider="gemini"',
|
||||
'data-effort="low"',
|
||||
'data-effort="medium"',
|
||||
'data-effort="high"',
|
||||
'data-skill-file="skill"',
|
||||
'data-skill-step="observe"',
|
||||
'data-skill-step="validate"',
|
||||
'data-common-skill="ponytail"',
|
||||
'data-common-skill="caveman"',
|
||||
'data-common-skill="unlazy"',
|
||||
'id="hands-on"',
|
||||
'data-copy-target="prompt-install-skills"',
|
||||
'data-copy-target="prompt-basic"',
|
||||
'data-copy-target="prompt-skills"',
|
||||
'hands-on/starter/',
|
||||
'additional-reading.md',
|
||||
'role="tablist"',
|
||||
'<table',
|
||||
])
|
||||
if (!html.includes(token)) throw new Error(`missing content ${token}`);
|
||||
for (const token of [
|
||||
'const phases',
|
||||
'const handsOnPrompts',
|
||||
'const modelGuide',
|
||||
'const skillSources',
|
||||
'const skillInstallPrompts',
|
||||
'addEventListener',
|
||||
"render('plan')",
|
||||
'renderTree',
|
||||
'renderWorker',
|
||||
'renderRoute',
|
||||
'renderModelProvider',
|
||||
'renderEffort',
|
||||
'renderSkillFile',
|
||||
'renderSkillWorkflow',
|
||||
'renderCommonSkill',
|
||||
'renderHandsOn',
|
||||
'copyPrompt',
|
||||
])
|
||||
if (!js.includes(token)) throw new Error(`missing interaction ${token}`);
|
||||
for (const token of ['id="task-list"', 'id="task-count"'])
|
||||
if (!starterHtml.includes(token)) throw new Error(`missing starter content ${token}`);
|
||||
for (const token of ['const tasks', 'renderTasks()'])
|
||||
if (!starterJs.includes(token)) throw new Error(`missing starter behavior ${token}`);
|
||||
for (const token of [
|
||||
'medium.com',
|
||||
'anthropic.com/engineering',
|
||||
'openai.com/business',
|
||||
'git-scm.com/docs/git-worktree',
|
||||
])
|
||||
if (!additional.includes(token)) throw new Error(`missing additional source ${token}`);
|
||||
if ((additional.match(/^### \d+\./gm) || []).length < 5)
|
||||
throw new Error('fewer than five additional readings');
|
||||
for (const token of [
|
||||
'e7b42dc2d384a702240dea4d52a7bf5530b821b6',
|
||||
'6654f6b60cd9d5be8b54c6fafe44346dabeb3b76',
|
||||
'53048666b05b4799081517d00e09e0a2dd688678',
|
||||
])
|
||||
if (!skillSources.includes(token) || !js.includes(token))
|
||||
throw new Error(`missing pinned skill source ${token}`);
|
||||
for (const token of [
|
||||
'developers.openai.com/api/docs/guides/latest-model',
|
||||
'docs.anthropic.com/en/docs/claude-code/model-config',
|
||||
'ai.google.dev/gemini-api/docs/thinking',
|
||||
])
|
||||
if (!modelRouting.includes(token) || !js.includes(token))
|
||||
throw new Error(`missing model source ${token}`);
|
||||
console.log('interaction verification passed');
|
||||
if (html.match(/<(script|link)[^>]+(src|href)="https?:[^"]+"/i))
|
||||
throw new Error('external runtime dependency found');
|
||||
console.log('standalone verification passed');
|
||||
for (const token of [
|
||||
'id="pipeline"',
|
||||
'id="skills"',
|
||||
'id="examples"',
|
||||
'data-stage="context"',
|
||||
'data-stage="cli"',
|
||||
'data-stage="commit"',
|
||||
'data-stage="review"',
|
||||
'gate-discipline',
|
||||
'parallel-agents',
|
||||
'repo-db',
|
||||
'tech-debt',
|
||||
'skill-writer',
|
||||
'scripts/check-ui-contract.mjs',
|
||||
'.husky/pre-commit',
|
||||
'.pr-review.json',
|
||||
'.agents/skills',
|
||||
'netcracker/interview',
|
||||
])
|
||||
if (!rulesHtml.includes(token)) throw new Error(`missing rules content ${token}`);
|
||||
console.log('rules content verification passed');
|
||||
for (const token of [
|
||||
'const languageCopy',
|
||||
'const stages',
|
||||
'const skills',
|
||||
'const prompts',
|
||||
'renderStage',
|
||||
'renderSkill',
|
||||
'renderLanguage',
|
||||
'copyPrompt',
|
||||
'addEventListener',
|
||||
])
|
||||
if (!rulesJs.includes(token)) throw new Error(`missing rules interaction ${token}`);
|
||||
for (const token of [
|
||||
'data-lang="en"',
|
||||
'data-lang="pt"',
|
||||
'data-copy-prompt',
|
||||
'aria-live="polite"',
|
||||
'role="tablist"',
|
||||
])
|
||||
if (!rulesHtml.includes(token)) throw new Error(`missing rules control ${token}`);
|
||||
console.log('rules interaction verification passed');
|
||||
if (!html.includes('href="../rules/"'))
|
||||
throw new Error('main presentation does not link to rules page');
|
||||
if (!html.includes('href="../skills-review/"'))
|
||||
throw new Error('main presentation does not link to skills review page');
|
||||
for (const token of ['../summary/', '../models/', '../agents/', '../skills/', 'chapter-route'])
|
||||
if (!html.includes(token)) throw new Error(`main presentation missing chapter route ${token}`);
|
||||
if (rulesHtml.includes('script src="http') || rulesHtml.includes('rel="stylesheet" href="http'))
|
||||
throw new Error('rules page has an external runtime dependency');
|
||||
for (const token of [
|
||||
'@media(min-width:2200px)',
|
||||
'@media(max-width:900px)',
|
||||
'@media(max-width:600px)',
|
||||
'prefers-reduced-motion',
|
||||
])
|
||||
if (!rulesCss.includes(token)) throw new Error(`missing rules responsive contract ${token}`);
|
||||
console.log('rules standalone verification passed');
|
||||
for (const token of [
|
||||
'id="catalog"',
|
||||
'id="skill-filter"',
|
||||
'id="skill-list"',
|
||||
'id="detail"',
|
||||
'Preview Markdown',
|
||||
'styles.css?v=20260904-vote-widget',
|
||||
'change-lens.css?v=20260904-vote-widget',
|
||||
'app.js?v=20260904-vote-widget',
|
||||
'?author=Name&skill=skill-id&view=improved',
|
||||
'SKILLS_REVIEW_VOTE_API',
|
||||
])
|
||||
if (!reviewHtml.includes(token)) throw new Error(`missing review page content ${token}`);
|
||||
for (const token of [
|
||||
"from './catalog.js'",
|
||||
"from './files.js'",
|
||||
'function renderList',
|
||||
'function renderDetail',
|
||||
'selectSkill',
|
||||
'packageSummary',
|
||||
'markdownHeadings',
|
||||
'markdownToc',
|
||||
"document.addEventListener('keydown'",
|
||||
'loadSelectedFile',
|
||||
'schedulePackageSearch',
|
||||
'fetchSource',
|
||||
'packageSearchText',
|
||||
'diffMarkup',
|
||||
'diffRows',
|
||||
'data-diff',
|
||||
"searchParams.set('compare'",
|
||||
'markdownMarkup',
|
||||
'data-render',
|
||||
'preview-markdown',
|
||||
'Preview Markdown',
|
||||
'View source',
|
||||
'FILE PREVIEW',
|
||||
"searchParams.set('render'",
|
||||
'AUTHOR ·',
|
||||
'SKILL ·',
|
||||
'function selectFromUrl',
|
||||
'function syncUrl',
|
||||
'URLSearchParams',
|
||||
'navigator.clipboard',
|
||||
'document.execCommand',
|
||||
'download',
|
||||
'data-file',
|
||||
"searchParams.set('file'",
|
||||
])
|
||||
if (!reviewJs.includes(token)) throw new Error(`missing review interaction ${token}`);
|
||||
for (const token of [
|
||||
'ndo-repro',
|
||||
'gfiber-logging',
|
||||
'confluence-page',
|
||||
'diagram-plantuml',
|
||||
'page-reviewer',
|
||||
'unslop',
|
||||
'spanish-naturalizer',
|
||||
'draft-mr',
|
||||
'semantic-diff-review',
|
||||
'reference.md',
|
||||
'files =',
|
||||
])
|
||||
if (!`${reviewFiles}\n${read('skills-review/submitted-files.js')}`.includes(token))
|
||||
throw new Error(`missing review file manifest ${token}`);
|
||||
const built = (path) => read(`dist/${path}`);
|
||||
const rendered = (value) =>
|
||||
value
|
||||
.replace(/<script[\s\S]*?<\/script>/gi, '')
|
||||
.replace(/<style[\s\S]*?<\/style>/gi, '')
|
||||
.replace(/<!--[\s\S]*?-->/g, '')
|
||||
.replace(/<[^>]+>/g, ' ')
|
||||
.replace(/&/g, '&')
|
||||
.replace(
|
||||
/&(lt|gt|quot|nbsp);/g,
|
||||
(_, entity) => ({ lt: '<', gt: '>', quot: '"', nbsp: ' ' })[entity],
|
||||
)
|
||||
.replace(/�?39;/g, "'")
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
const normalize = (value) => value.replace(/\s+/g, ' ').trim();
|
||||
|
||||
const html = {
|
||||
landing: built('index.html'),
|
||||
guide: built('full-guide/index.html'),
|
||||
summary: built('summary/index.html'),
|
||||
models: built('models/index.html'),
|
||||
agents: built('agents/index.html'),
|
||||
skills: built('skills/index.html'),
|
||||
rules: built('rules/index.html'),
|
||||
review: built('skills-review/index.html'),
|
||||
starter: built('hands-on/starter/index.html'),
|
||||
labRules: built('hands-on/rules/index.html'),
|
||||
};
|
||||
const snapshot = (path) => normalize(read(`.agents/snapshots/${path}`));
|
||||
const reviewAssetSrc = html.review.match(/src="([^"]+\.[a-zA-Z0-9_-]{8,}\.js)"/)?.[1];
|
||||
const reviewAsset = reviewAssetSrc ? read(`dist/_astro/${reviewAssetSrc.split('/').at(-1)}`) : '';
|
||||
|
||||
// Rendered prose catches a deleted paragraph that string-token checks miss.
|
||||
if (rendered(html.landing) !== snapshot('index.txt'))
|
||||
throw new Error('landing rendered-text snapshot changed');
|
||||
if (rendered(html.guide) !== snapshot('full-guide.txt'))
|
||||
throw new Error('full-guide rendered-text snapshot changed');
|
||||
if (rendered(html.summary) !== snapshot('summary.txt'))
|
||||
throw new Error('summary rendered-text snapshot changed');
|
||||
if (rendered(html.models) !== snapshot('models.txt'))
|
||||
throw new Error('models rendered-text snapshot changed');
|
||||
if (rendered(html.agents) !== snapshot('agents.txt'))
|
||||
throw new Error('agents rendered-text snapshot changed');
|
||||
if (rendered(html.skills) !== snapshot('skills.txt'))
|
||||
throw new Error('skills rendered-text snapshot changed');
|
||||
if (rendered(html.rules) !== snapshot('rules.txt'))
|
||||
throw new Error('rules rendered-text snapshot changed');
|
||||
if (rendered(html.review) !== snapshot('skills-review.txt'))
|
||||
throw new Error('skills-review rendered-text snapshot changed');
|
||||
if (rendered(html.starter) !== snapshot('hands-on/starter.txt'))
|
||||
throw new Error('starter rendered-text snapshot changed');
|
||||
if (rendered(html.labRules) !== snapshot('hands-on/rules.txt'))
|
||||
throw new Error('rules lab rendered-text snapshot changed');
|
||||
|
||||
const braceMatch = (source, open) => {
|
||||
let depth = 0;
|
||||
let quote = '';
|
||||
let escaped = false;
|
||||
for (let index = open; index < source.length; index += 1) {
|
||||
const character = source[index];
|
||||
if (quote) {
|
||||
if (escaped) escaped = false;
|
||||
else if (character === '\\') escaped = true;
|
||||
else if (character === quote) quote = '';
|
||||
continue;
|
||||
}
|
||||
if (character === "'" || character === '"' || character === '`') quote = character;
|
||||
else if (character === '{') depth += 1;
|
||||
else if (character === '}' && --depth === 0) return index;
|
||||
}
|
||||
throw new Error('could not brace-match legacy Portuguese translations');
|
||||
};
|
||||
const legacyGuide = read('app.js');
|
||||
const translationsStart = legacyGuide.indexOf('const translations =');
|
||||
const objectStart = legacyGuide.indexOf('{', translationsStart);
|
||||
const objectEnd = braceMatch(legacyGuide, objectStart);
|
||||
const translations = Function(`return (${legacyGuide.slice(objectStart, objectEnd + 1)})`)();
|
||||
const portuguese = Object.values(translations.pt).map(rendered);
|
||||
if (portuguese.length !== 102)
|
||||
throw new Error('full-guide Portuguese source no longer has 102 translated strings');
|
||||
if (!portuguese.every((value) => rendered(html.guide).includes(value)))
|
||||
throw new Error('built full-guide lost a Portuguese translation');
|
||||
|
||||
const has = (page, tokens) => tokens.every((token) => page.includes(token));
|
||||
if (!has(html.guide, ['data-phase="plan"', 'data-phase="build"', 'data-phase="review"']))
|
||||
throw new Error('built full-guide lost its phase selector');
|
||||
if (!has(html.guide, ['data-tree="main"', 'data-tree="ui"', 'data-worker="ui"']))
|
||||
throw new Error('built full-guide lost its worker or worktree selector');
|
||||
if (
|
||||
(reviewCatalog.match(/id:'/g) || []).length +
|
||||
(read('skills-review/submitted-catalog.js').match(/id:'/g) || []).length !==
|
||||
24
|
||||
!has(html.guide, [
|
||||
'data-route="plan"',
|
||||
'data-model-provider="openai"',
|
||||
'data-model-provider="claude"',
|
||||
'data-model-provider="gemini"',
|
||||
])
|
||||
)
|
||||
throw new Error('review catalog does not cover all submissions');
|
||||
throw new Error('built full-guide lost its model-routing controls');
|
||||
if (!has(html.guide, ['data-effort="low"', 'data-effort="medium"', 'data-effort="high"']))
|
||||
throw new Error('built full-guide lost its reasoning-effort controls');
|
||||
if (
|
||||
!reviewCatalog.includes('hardcoded password') ||
|
||||
!reviewCatalog.includes('safety-redacted') ||
|
||||
!reviewJs.includes('[REDACTED]') ||
|
||||
!reviewJs.includes('[REDACTED LOCAL USER]') ||
|
||||
!reviewJs.includes('[REDACTED USER]')
|
||||
!has(html.guide, [
|
||||
'data-skill-file="skill"',
|
||||
'data-skill-step="observe"',
|
||||
'data-skill-step="validate"',
|
||||
])
|
||||
)
|
||||
throw new Error('review catalog does not record secret safety handling');
|
||||
console.log('skills review verification passed');
|
||||
for (const page of [summaryHtml, modelsHtml, agentsHtml, skillsHtml])
|
||||
if (!page.includes('../chapters.css') || !page.includes('ROUTE MAP'))
|
||||
throw new Error('chapter page missing shared navigation');
|
||||
for (const token of ['--ink', '@media(max-width:800px)', '@media(max-width:520px)'])
|
||||
if (!chaptersCss.includes(token)) throw new Error(`missing chapter responsive contract ${token}`);
|
||||
console.log('chapter route verification passed');
|
||||
for (const token of [
|
||||
'The short route',
|
||||
'full-guide/',
|
||||
'models/',
|
||||
'agents/',
|
||||
'skills/',
|
||||
'rules/',
|
||||
'hands-on/starter/',
|
||||
'skills-review/',
|
||||
])
|
||||
if (!landingHtml.includes(token)) throw new Error(`landing page missing ${token}`);
|
||||
if (landingHtml.includes('app.js'))
|
||||
throw new Error('landing page should remain a fast, static route map');
|
||||
console.log('landing route verification passed');
|
||||
for (const token of [
|
||||
'data-package-file="skill"',
|
||||
'data-package-file="references"',
|
||||
'data-package-file="scripts"',
|
||||
'data-package-file="assets"',
|
||||
'id="package-preview"',
|
||||
'script src="app.js"',
|
||||
])
|
||||
if (!skillsHtml.includes(token)) throw new Error(`skills anatomy missing ${token}`);
|
||||
const skillsApp = read('skills/app.js');
|
||||
const skillsCss = read('skills/styles.css');
|
||||
for (const token of [
|
||||
'const packageFiles',
|
||||
'function renderPackage',
|
||||
'addEventListener',
|
||||
"renderPackage('skill')",
|
||||
])
|
||||
if (!skillsApp.includes(token)) throw new Error(`skills anatomy interaction missing ${token}`);
|
||||
for (const token of [
|
||||
'grid-template-columns:minmax(190px',
|
||||
'overflow-wrap:anywhere',
|
||||
'@media(max-width:800px)',
|
||||
'prefers-reduced-motion',
|
||||
])
|
||||
if (!skillsCss.includes(token))
|
||||
throw new Error(`skills anatomy responsive contract missing ${token}`);
|
||||
console.log('skills anatomy verification passed');
|
||||
console.log('presentation verification passed');
|
||||
const legacyName = String.fromCharCode(80, 101, 100, 114, 111, 32, 65, 114, 97, 110, 104, 97);
|
||||
const legacyHandle = legacyName.toLowerCase().replace(' ', '.');
|
||||
for (const path of [
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/SKILL.md',
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/envs.tsv',
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/lib/env.sh',
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-api.sh',
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/ndo-ship.sh',
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/bom-Dockerfile_local.example',
|
||||
'submitted-skills/Anonymous Operational Submission/skills/ndo-repro/reference/dockerfile-local.md',
|
||||
'skill-reviews/improved/ndo-repro/SKILL.md',
|
||||
]) {
|
||||
const source = read(path);
|
||||
if (
|
||||
new RegExp(
|
||||
`${legacyName}|${legacyHandle}|https?:\\/\\/|git\\.netcracker\\.com|artifactorycn|managed\\.netcracker\\.cloud`,
|
||||
'i',
|
||||
).test(source)
|
||||
throw new Error('built full-guide lost its skill anatomy controls');
|
||||
if (
|
||||
!has(html.guide, [
|
||||
'data-common-skill="ponytail"',
|
||||
'data-common-skill="caveman"',
|
||||
'data-common-skill="unlazy"',
|
||||
])
|
||||
)
|
||||
throw new Error('built full-guide lost its common-skill controls');
|
||||
if (
|
||||
!has(html.guide, [
|
||||
'id="hands-on"',
|
||||
'data-copy-target="prompt-install-skills"',
|
||||
'data-copy-target="prompt-basic"',
|
||||
'data-copy-target="prompt-skills"',
|
||||
])
|
||||
)
|
||||
throw new Error('built full-guide lost its hands-on prompts');
|
||||
if (!has(html.guide, ['hands-on/starter/', 'additional-reading.md', 'role="tablist"', '<table']))
|
||||
throw new Error('built full-guide lost its guide navigation or semantics');
|
||||
if (!html.guide.includes('<script type="module">'))
|
||||
throw new Error('built full-guide lost its interaction module');
|
||||
|
||||
if (
|
||||
!has(html.rules, [
|
||||
'id="pipeline"',
|
||||
'id="skills"',
|
||||
'id="examples"',
|
||||
'data-stage="context"',
|
||||
'data-stage="cli"',
|
||||
'data-stage="commit"',
|
||||
'data-stage="review"',
|
||||
])
|
||||
)
|
||||
throw new Error('built rules page lost its enforcement pipeline');
|
||||
if (
|
||||
!has(html.rules, [
|
||||
'gate-discipline',
|
||||
'parallel-agents',
|
||||
'repo-db',
|
||||
'tech-debt',
|
||||
'skill-writer',
|
||||
'scripts/check-ui-contract.mjs',
|
||||
'.husky/pre-commit',
|
||||
'.pr-review.json',
|
||||
'.agents/skills',
|
||||
'netcracker/interview',
|
||||
])
|
||||
)
|
||||
throw new Error('built rules page lost its case-study content');
|
||||
if (
|
||||
!has(html.rules, [
|
||||
'data-lang="en"',
|
||||
'data-lang="pt"',
|
||||
'data-copy-prompt',
|
||||
'aria-live="polite"',
|
||||
'role="tablist"',
|
||||
])
|
||||
)
|
||||
throw new Error('built rules page lost its accessible controls');
|
||||
if (!html.rules.includes('data-rules-copy') || !html.rules.includes('"pt"'))
|
||||
throw new Error('built rules page lost its Portuguese interaction data');
|
||||
|
||||
if (
|
||||
!has(html.review, [
|
||||
'id="catalog"',
|
||||
'id="skill-filter"',
|
||||
'id="skill-list"',
|
||||
'id="detail"',
|
||||
'Preview Markdown',
|
||||
'?author=Name&skill=skill-id&view=improved',
|
||||
'SKILLS_REVIEW_VOTE_API',
|
||||
])
|
||||
)
|
||||
throw new Error('built review desk lost its catalog or deep-link contract');
|
||||
if (
|
||||
!has(html.review, [
|
||||
'ndo-repro',
|
||||
'gfiber-logging',
|
||||
'confluence-page',
|
||||
'diagram-plantuml',
|
||||
'page-reviewer',
|
||||
'unslop',
|
||||
'spanish-naturalizer',
|
||||
'draft-mr',
|
||||
'semantic-diff-review',
|
||||
])
|
||||
)
|
||||
throw new Error('built review desk lost submitted-skill coverage');
|
||||
if (
|
||||
!html.review.includes('Anonymous%20Operational%20Submission') ||
|
||||
!has(reviewAsset, ['[REDACTED]', '[REDACTED LOCAL USER]', '[REDACTED USER]'])
|
||||
)
|
||||
throw new Error('built review desk lost operational-submission privacy redactions');
|
||||
if (!reviewAssetSrc) throw new Error('built review desk does not load its hashed island asset');
|
||||
|
||||
if (
|
||||
![html.summary, html.models, html.agents, html.skills].every((page) => page.includes('ROUTE MAP'))
|
||||
)
|
||||
throw new Error('built chapter pages lost shared navigation');
|
||||
if (
|
||||
!has(html.landing, [
|
||||
'The short route',
|
||||
'full-guide/',
|
||||
'models/',
|
||||
'agents/',
|
||||
'skills/',
|
||||
'rules/',
|
||||
'hands-on/starter/',
|
||||
'skills-review/',
|
||||
])
|
||||
)
|
||||
throw new Error('built landing page lost a route-map destination');
|
||||
if (/<script\b/i.test(html.landing))
|
||||
throw new Error('built landing page is no longer a static route map');
|
||||
if (
|
||||
!has(html.skills, [
|
||||
'data-skill-file="skill"',
|
||||
'data-skill-file="references"',
|
||||
'data-skill-file="scripts"',
|
||||
'data-skill-file="assets"',
|
||||
'id="package-preview"',
|
||||
])
|
||||
)
|
||||
throw new Error('built skills page lost its package explorer');
|
||||
if (!has(html.starter, ['id="task-list"', 'id="task-count"']))
|
||||
throw new Error('starter lab lost its task-list contract');
|
||||
|
||||
const allPages = Object.entries(html);
|
||||
if (!allPages.every(([, page]) => page.includes('name="viewport"')))
|
||||
throw new Error('a built route lacks a viewport declaration');
|
||||
if (allPages.some(([, page]) => /<(script|link)[^>]+(src|href)="https?:[^\"]+"/i.test(page)))
|
||||
throw new Error('a built route has an external runtime dependency');
|
||||
if (
|
||||
!read('package.json').includes(
|
||||
'"verify": "node scripts/verify.mjs && node scripts/audit-ui.mjs && node .agents/scripts/check-tokens.mjs"',
|
||||
)
|
||||
throw new Error(`operational submission privacy leak in ${path}`);
|
||||
}
|
||||
for (const source of [reviewCatalog, reviewFiles, reviewJs])
|
||||
if (source.includes(legacyName) || source.includes(legacyName.replace(' ', '%20')))
|
||||
throw new Error('operational submission identity remains in review desk source');
|
||||
if (
|
||||
!reviewFiles.includes('Anonymous%20Operational%20Submission') ||
|
||||
!reviewJs.includes("entry.id === 'ndo-repro'")
|
||||
)
|
||||
throw new Error('operational submission redaction contract missing');
|
||||
console.log('review privacy verification passed');
|
||||
for (const token of [
|
||||
'currentContent',
|
||||
'unchangedDraft',
|
||||
'state.preview = button.dataset.preview',
|
||||
'state.file = available.find',
|
||||
'loadSelectedFile',
|
||||
])
|
||||
if (!reviewJs.includes(token)) throw new Error(`review file-mode contract missing ${token}`);
|
||||
if (reviewJs.includes("state.preview = 'original'; syncUrl(); renderDetail(); loadSelectedFile();"))
|
||||
throw new Error('file selection still resets improved mode');
|
||||
console.log('review file-mode verification passed');
|
||||
for (const token of [
|
||||
'const changeRows',
|
||||
'function lensMarkup',
|
||||
'data-lens',
|
||||
'CHANGE LENS',
|
||||
'What changed — and why.',
|
||||
])
|
||||
if (!reviewJs.includes(token)) throw new Error(`review change-lens contract missing ${token}`);
|
||||
for (const token of [
|
||||
'.change-lens',
|
||||
'.change-rows',
|
||||
'.skill-diff',
|
||||
'.diff-lines',
|
||||
'@media(max-width:620px)',
|
||||
'prefers-reduced-motion',
|
||||
])
|
||||
if (!reviewLensCss.includes(token)) throw new Error(`review change-lens CSS missing ${token}`);
|
||||
console.log('review change-lens verification passed');
|
||||
for (const token of [
|
||||
'.markdown-preview',
|
||||
'max-height:540px',
|
||||
'.markdown-table-wrap',
|
||||
'.markdown-frontmatter',
|
||||
'.markdown-toc',
|
||||
'.preview-title',
|
||||
'.preview-markdown',
|
||||
'grid-template-columns:minmax(0,1fr)',
|
||||
'height:120px',
|
||||
'-webkit-line-clamp:2',
|
||||
])
|
||||
if (!read('skills-review/styles.css').includes(token))
|
||||
throw new Error(`review markdown preview contract missing ${token}`);
|
||||
console.log('review markdown preview verification passed');
|
||||
for (const token of ["from './vote.js'", "renderVoteWidget($('#vote-widget'"])
|
||||
if (!reviewJs.includes(token)) throw new Error(`review vote widget wiring missing ${token}`);
|
||||
for (const token of [
|
||||
'id="vote-widget"',
|
||||
'function renderVoteWidget',
|
||||
'X-Voter-Id',
|
||||
'/api/votes',
|
||||
'Voting is offline',
|
||||
])
|
||||
if (!reviewVoteJs.includes(token) && !reviewJs.includes(token))
|
||||
throw new Error(`review vote widget contract missing ${token}`);
|
||||
for (const token of ['.vote-widget', '.vote-buttons', '[aria-pressed="true"]'])
|
||||
if (!read('skills-review/styles.css').includes(token))
|
||||
throw new Error(`review vote widget CSS missing ${token}`);
|
||||
throw new Error('pnpm verify no longer runs audit-ui and check-tokens');
|
||||
if (!read('.agents/scripts/gate.sh').includes('pnpm run verify'))
|
||||
throw new Error('the gate no longer runs the output contract');
|
||||
if (!read('docs/references/README.md').includes('https://developers.openai.com/codex/skills'))
|
||||
throw new Error('primary reference list lost Codex skills');
|
||||
if ((read('docs/references/additional-reading.md').match(/^### \d+\./gm) || []).length < 5)
|
||||
throw new Error('additional reading list has fewer than five entries');
|
||||
if (
|
||||
!voteService.includes('X-Forwarded-For') ||
|
||||
(!voteService.includes('one active vote per skill') &&
|
||||
!voteService.includes('at most one active vote'))
|
||||
/Pedro\s+Aranha|pedro\.aranha|git\.netcracker\.com|artifactorycn|managed\.netcracker\.cloud/i.test(
|
||||
read('src/content/reviews/ndo-repro.md'),
|
||||
)
|
||||
)
|
||||
throw new Error('vote-service missing IP-based one-vote-per-source contract');
|
||||
console.log('review vote widget verification passed');
|
||||
throw new Error('operational review content has a privacy leak');
|
||||
|
||||
console.log('built output verification passed');
|
||||
|
||||
Reference in New Issue
Block a user