feat: contract rule IDs, a docs checker and a kb-docs-add audit mode; fix stale pierced CSS - #100
Merged
Merged
Conversation
Embedded with server-side piercing, reframed copies every linked sub-app stylesheet into an adopted constructed stylesheet when it portals the fragment, and only drops the copy once the sheet is fetched again. On the moveBefore() path it never is, so the first app's CSS stayed applied to the catalog and to every other app for the life of the fragment. The copy also resolves url() against the host document, whose router strips the trailing slash, so a relative url() in a sub-app stylesheet 404'd (/knowledge-base/docs/... instead of /knowledge-base/user-guide/docs/...). - copyAssets() now makes every url() and @import in copied sub-app CSS absolute, resolving relative ones against the stylesheet's own URL (rewriteCssUrls(), shared with the inline-style rewrite). - embedded-transitions.js drops the knowledge base's own adopted copies before the first swap, identified by the layer-order statement every built sub-app stylesheet opens with. Serving scripts and styles from a separate path prefix was evaluated and rejected: the gateway dispatches on sec-fetch-dest, not on the path, and a retargeted build reproduced both failures unchanged. Tests: relative url() rewrite (unit + dist scan), and an embedded test that starts on an app page, crosses to another app, and asserts no copy remains, styles match a hard load and no request fails. Both fail without the fix. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
contract/RULES.md is the catalogue: every rule a published docs site is checked against, by ID (KB-<area>-<nnn>), with its severity, the reason and the fix. actions/lib/rules.js is the machine side, and the self-test fails if the two disagree on IDs, titles or severities. actions/lib/check.js replaces verify-html.js. It parses HTML (htmlparser2) and CSS (postcss) instead of pattern matching, so markup quoted in prose, data blocks and article headers are not reported. Every finding names its rule; errors stop the publish, warnings are annotated with the rule as the title. The existing checks keep their severity and wording, now prefixed with the ID; manifest and size failures carry KB-MAN-001 / KB-ART-00x. New rules, all warnings so no repo pinned to @v1 starts failing: inline event handlers and javascript: URLs (dead under script-src 'self'), a site-level <header>, links to non-HTML files (a document request for one is answered by the host application), !important (beats the kb-app layer fence), dark mode, scripts/styles/fonts from another origin (CSP), and fetches of page-relative URLs (resolved against the host page once embedded). actions/lib/check-cli.js runs the same checks without publishing, for a docs repo's PR CI or an agent fixing a repo (--json, --strict). isThemeBootstrap() moves to the dependency-free src/utils/theme.js so the build and the checker share one definition. htmlparser2 and postcss become direct dependencies of actions/ at the versions its lockfile already carried. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An already-onboarded repo (it has kb-docs.json, or a workflow calling publish-docs / publish-single-page-docs) now goes down an audit path instead of onboarding. references/audit.md: 1. check the workflow wiring the checker cannot see (pinned @v1, contents: write, the headless variant is what gets built, dist); 2. build what the workflow builds, or download the last release; 3. run actions/lib/check-cli.js from a scratch clone at the pinned major, never copied into the repo; 4. fix each finding at its source, errors first, from a table mapping every rule ID to its usual fix; never edit built output or patch a theme the team does not own; 5. read the site's own scripts for what the checker cannot see (URLs built from location, client-side routing) and report those; 6. re-check and report every finding with a status. Onboarding's Verify step and packaged.md now run the checker instead of three greps. Eval 4 (onboarded-mkdocs) is a real mkdocs repo with planted violations: a workflow that builds the standalone site, an inline dark-mode bootstrap, an onclick handler, Google Fonts, !important, a page-relative fetch, a PDF link and a location.pathname-derived base. Built with mkdocs 1.6.1 the checker reports 3 errors and 18 warnings; the fixes audit.md prescribes bring it to no findings. Two agent runs following only the skill: the first met 12 of 13 assertions (the miss was an over-strict assertion, now relaxed) and its feedback shaped audit.md; the second met all 14. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
oto-macenauer-absa
added a commit
that referenced
this pull request
Sep 23, 2026
Follow-up to #100. The knowledge base build now runs the same contract checker the `publish-docs` action runs (`actions/lib/check.js`, rules in `contract/RULES.md`), on every artifact it installs. ## Why Not every artifact in a registry went through a current action: - one published before a rule existed; - one packed by hand; - one whose HTML the old v1.0.0 pattern-matching missed, for example a root-relative URL in a single-quoted attribute or in `formaction`. The build repairs some findings as it re-hosts a page, so a failing publish is not the only signal a registry owner needs. They need to see which docs repo to chase, and for what. ## What - **`scripts/check-artifact.js`** checks each app as installed into `apps/`, before inline scripts are hoisted, on every source (prebuilt and local included). - Findings are logged grouped by rule: one line per rule with a count and the first example, then a per-artifact summary. - Example from the vendored fixture: `KB-HTML-004 ×9, e.g. user-guide/admin/index.html: …`, then `0 error(s), 28 warning(s)`. - **A strict build refuses an artifact with an `error` finding.** Strict is `KB_STRICT`, which the deployment workflow turns on by default. The error names the rule and the file. Warnings never fail a build. - **Replaced:** the build's old headless-marker warning, which only looked at the entry page. - **Dependencies:** `htmlparser2` and `postcss` are now root dependencies, pinned at the versions `actions/package.json` pins. - `check.js` resolves them from `actions/node_modules` when that is installed, otherwise from the root. - CI's E2E job installs only the root. - `tests/artifact-checks.spec.js` fails if the two pins drift. ##⚠️ Deployment impact Strict is on by default in `build-image.yml`, so after this merges a deployment build fails if any registered artifact has an error finding. Artifacts published by the v1 action should pass: the action has refused these error rules since v1.0.0. The remaining risk is: - an artifact that did not come from the action; - a pattern the old checks missed. To check a deployment registry before rolling this out, run a non-strict build of it. It logs every finding without failing. ## Testing - `npm test`: 382 of 382 passed, including the 10 new tests. - `npm run selftest` in `actions/` passes. - **Checked the root-only path, as CI's E2E job has it:** with `actions/node_modules` moved aside, the hermetic build and `artifact-checks.spec.js` both pass. - `contract/DEPLOYMENT.md` gains the new strict-rejection row, and `RULES.md` and `CLAUDE.md` are updated. Refs #67. The build now logs inline handlers (`KB-HTML-005`) per artifact; it still does not strip them. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #99. There are three commits, and each one can be reviewed on its own.
1.
fix: absolute CSS URLs, and no stale portal copies in a pierced fragmentMeasured with the embedded harness in all three modes: pierced, bound and unbound.
moveBefore()path the<link>is never re-fetched, so the copy stays for the life of the fragment.embedded-transitions.jsnow drops the knowledge base's own copies before the first swap. It recognises them by the layer-order statement that every built sub-app stylesheet opens with.url()in sub-app CSS returned 404s. The copy resolves URLs against the host document, and the host router strips the trailing slash. Soshowcase.cssrequested/knowledge-base/docs/assets/images/hero.png.copyAssets()now makes everyurl()and@importabsolute, resolved against the stylesheet's own URL. It usesrewriteCssUrls(), shared with the inline-style rewrite.sec-fetch-dest, not by path. A build retargeted to/__wf/knowledge-base/reproduced both failures unchanged.2.
feat(contract): rule IDs and a checker for published docscontract/RULES.mdlists every rule a published docs site is checked against, by ID (KB-HTML-003…), with its severity, the reason and the fix.actions/lib/rules.jsis the code side, and the self-test fails if the two disagree.actions/lib/check.jsreplacesverify-html.js. It parses HTML with htmlparser2 and CSS with postcss. The existing checks keep their severity and wording, now prefixed with the rule ID.@v1starts failing:on*handlers andjavascript:URLs, which never run underscript-src 'self';<header>;!important;actions/lib/check-cli.jsruns the same checks without publishing, for a docs repo's PR CI or an agent. It supports--jsonand--strict.3.
feat(skills): audit mode forkb-docs-addAn already-onboarded repo now takes the audit path in
references/audit.md:Testing
npm test: 372 of 372 passed on the tree for commits 1 and 2. Commit 3 only touches the skill, its fixtures andCLAUDE.md, andtests/skill.spec.jspasses on it (30 of 30).build-integrity(relativeurl()) andcss-isolation("starting on an app page").npm run selftestinactions/passes. It includes the newcheck.selftest.js: one case per rule, a clean site that must produce no findings, the CLI, and the RULES.md ↔ rules.js sync check.docs-examplefixture: 0 errors, 28 genuine warnings;tests/fixtures/kb-docs-add/onboarded-mkdocsis an onboarded mkdocs repo with problems planted in it.Notes for reviewers
v1.xtag movesv1. Until then,audit.mdtells agents to clone without--branch.htmlparser2andpostcssbecome direct dependencies ofactions/, at the versions its lockfile already carried.actions/dependencies, so that needs a decision on how.Refs #67. The checker now reports inline handlers (
KB-HTML-005), but the build still does not strip them.🤖 Generated with Claude Code