diff --git a/CLAUDE.md b/CLAUDE.md
index e408596..e876a27 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -83,6 +83,7 @@ Orchestrator: `scripts/build-vite.js`. Flags: `--local`, `--headless`.
- `scripts/build-vite.js` — Build orchestrator (4 steps: prepare, hoist, copy assets, astro build)
- `scripts/fetch-apps.js` — GitHub Release artifact downloader. Only *obtains* an artifact; installing it is one shared path in `build-vite.js`
- `scripts/artifacts.js` — Safe tarball extraction + tree copy, shared by both fetch paths. Validates archive members (no traversal, no absolute paths, no symlinks) before anything is written, and replaces the old `cp -r`/`tar` shell-outs so the build runs on Windows
+- `scripts/check-artifact.js` — Runs the publish action's contract checker (`actions/lib/check.js`) on every installed artifact, before hoisting; logs findings grouped by rule, and fails a strict build on an error. `check.js` resolves its parsers from `actions/node_modules` or the root, which pins the same versions
- `scripts/hoist-inline-scripts.js` — Moves inline `'),
+ 'guide/index.html': page(undefined, ''),
+ });
+ try {
+ const lines = summarise(checkApp(dir, ENTRY));
+ expect(lines).toHaveLength(1);
+ expect(lines[0].id).toBe('KB-HTML-004');
+ expect(lines[0].line).toMatch(/^KB-HTML-004 ×2, e\.g\. demo\/\S+\.html: 1 inline ') });
+ const clean = app({ 'index.html': page() });
+ try {
+ const warned = [];
+ expect(reportFindings('owner/docs', checkApp(noisy, ENTRY), { strict: true, warn: (m) => warned.push(m) }))
+ .toEqual({ errors: 0, warnings: 1 });
+ expect(warned.length).toBeGreaterThan(0);
+
+ const silent = [];
+ expect(reportFindings('owner/docs', checkApp(clean, ENTRY), { strict: true, warn: (m) => silent.push(m) }))
+ .toEqual({ errors: 0, warnings: 0 });
+ expect(silent).toEqual([]);
+ } finally {
+ rmSync(noisy, { recursive: true, force: true });
+ rmSync(clean, { recursive: true, force: true });
+ }
+ });
+});