diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index dfd20ee..d647ebd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,7 +72,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: '20' + node-version: '22' - name: Build site run: node scripts/build.js @@ -86,36 +86,36 @@ jobs: fi - name: Run skill-a11y-audit (WCAG 2.1 AA) - uses: snapsynapse/skill-a11y-audit/.github/actions/scan@main + uses: snapsynapse/skill-a11y-audit/.github/actions/scan@6af2c95a56ff058cddf0c6febc71512db1d6d19b # v3.1.0 with: serve-path: ./docs sitemap: http://localhost:8088/sitemap.xml sitemap-find: ${{ steps.site.outputs.base }} sitemap-replace: http://localhost:8088 - fail-on: errors + fail-on: major - name: Audit canonical root landing if: github.repository == 'snapsynapse/knowledge-as-code-template' - uses: snapsynapse/skill-a11y-audit/.github/actions/scan@main + uses: snapsynapse/skill-a11y-audit/.github/actions/scan@6af2c95a56ff058cddf0c6febc71512db1d6d19b # v3.1.0 with: serve-path: ./ port: 8089 sitemap: http://localhost:8089/tests/fixtures/a11y-root-sitemap.xml sitemap-find: https://knowledge-as-code.com sitemap-replace: http://localhost:8089 - fail-on: errors + fail-on: major artifact-name: a11y-root-results - name: Audit canonical demo deployment if: github.repository == 'snapsynapse/knowledge-as-code-template' - uses: snapsynapse/skill-a11y-audit/.github/actions/scan@main + uses: snapsynapse/skill-a11y-audit/.github/actions/scan@6af2c95a56ff058cddf0c6febc71512db1d6d19b # v3.1.0 with: serve-path: ./demo port: 8090 sitemap: http://localhost:8090/sitemap.xml sitemap-find: https://knowledge-as-code.com/demo sitemap-replace: http://localhost:8090 - fail-on: errors + fail-on: major artifact-name: a11y-demo-results # GitHub Pages serves directly from `main` branch root on this canonical repo. diff --git a/CHANGELOG.md b/CHANGELOG.md index 39b1fbc..7f6231f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ pattern. ## [Unreleased] +### Changed +- Pinned all three accessibility scan surfaces to A11y Audit v3.1.0 at its verified immutable commit, selected Node.js 22 explicitly for the scanner, and adopted the fail-closed major-findings policy. + +### Fixed +- Repaired shared generated-site and canonical landing contrast, link distinction, decorative marker, matrix-label, and theme-control behavior found while scanning all 45 required routes. + ## [1.2.0] - 2026-09-05 ### Added diff --git a/MAINTENANCE.md b/MAINTENANCE.md index 481d856..6d6142f 100644 --- a/MAINTENANCE.md +++ b/MAINTENANCE.md @@ -16,6 +16,10 @@ The maintained public path is: The current supported runtime is Node.js 18 or later on macOS and Linux. Windows may work but is not part of the tested support promise. +## Accessibility gate + +The canonical CI scans the generated template, canonical root landing, and canonical demo with the immutable A11y Audit identity and fail-closed major-findings policy recorded in [the 2026-09-07 accessibility adoption evidence](audits/a11y-2026-09-07.md). The generator keeps its Node.js 18-or-later support promise; the accessibility job selects its scanner's required Node.js 22 runtime explicitly. + ## Maintenance priorities Work is prioritized in this order: diff --git a/MANIFEST.yaml b/MANIFEST.yaml index 48a962c..42cd8b3 100644 --- a/MANIFEST.yaml +++ b/MANIFEST.yaml @@ -82,7 +82,7 @@ files: - path: scripts/build.js role: tooling - hash: sha256:0b9445770253923bee61b49b90a883bf3db3a6511777b9da928ae26022f8382a + hash: sha256:ad7861d1350c55c69294d08bbaa175ffe9b11b53139f17b8ac7ebed58bea3191 note: Static site generator - path: scripts/assets/favicon.svg @@ -122,7 +122,7 @@ files: - path: scripts/eval.js role: tooling - hash: sha256:7f5622d2245588bf7cdf00cd2afcf651f7b9a44565b3572645b931bdfcfd0e49 + hash: sha256:f87c45678844465c5d84bb838664802cfa3aeaab397f3b60cddda33a6ad4a043 note: Behavioral and security regression suite - path: mcp-server.js @@ -157,7 +157,7 @@ files: - path: index.html role: protocol - hash: sha256:8a431b58a12102a94602548fa3810ce348e10b5e2701e0a708649c1c78c595f7 + hash: sha256:2c13538b1d72fb4b365f54e77a41a9697fc77f71d9ab26622e55e47147f9f8db note: Canonical landing and executable quick start - path: sitemap.xml @@ -182,7 +182,7 @@ files: - path: MAINTENANCE.md role: protocol - hash: sha256:0d1fde34cb47dc6a25e71ff9bbfa28adb23ebf41bc99167850d919369881457d + hash: sha256:51a5afe6c658692a7d1789dc695d64451ba7c4660719e43a6776b1ef03c8e4fa note: Compatibility and upgrade policy - path: VERIFICATION.md diff --git a/audits/a11y-2026-09-07.md b/audits/a11y-2026-09-07.md new file mode 100644 index 0000000..daec18d --- /dev/null +++ b/audits/a11y-2026-09-07.md @@ -0,0 +1,47 @@ +# Accessibility adoption evidence + +Date: 2026-09-07 + +## Action and policy identity + +The three required accessibility scans use A11y Audit v3.1.0 at immutable commit `6af2c95a56ff058cddf0c6febc71512db1d6d19b`. The public annotated tag `refs/tags/v3.1.0` resolves through tag object `1fccece25b28c99bcbbf26881edc336c586f6d5e` to that commit. This relationship was independently verified from the public Git remote before adoption. + +The accessibility job selects Node.js 22 explicitly. The action requires Node.js 22.12.0 or later and runs axe-core 4.12.1 through Puppeteer 25.10.0. Local acceptance used Node.js 26.7.0, which satisfies that runtime floor, with the same scanner dependencies from the v3.1.0 source checkout. + +All three scans use `fail-on: major` without a baseline. Confirmed critical or serious standards violations block. Moderate, minor, and best-practice-only findings remain advisory. Unknown violation impact, potentially major incomplete evidence, missing coverage, and scan errors cannot pass. + +## Built surfaces and route coverage + +The tested source was the clean v1.2.0 baseline at `51abb92806393ae4ff0e6e671e77fe383ae0f690` plus the reviewed accessibility changes described below. + +| Surface | Build or source identity | Required routes | Result | Result SHA-256 | +|---|---|---:|---|---| +| Template default | `node scripts/build.js`, transient `docs/`, `https://example.com/` rewritten to the local origin | 22/22 sitemap routes | Pass | `67e34ff3d895103d4edbbfebe74f795cd1fb711779e075e8d3593ce914d8cfec` | +| Canonical root landing | Hand-maintained root `index.html`, root fixture sitemap, Pages root unchanged | 1/1 reviewed route | Pass | `e1492560ce7ae26b34d57ef3f3558871b2013d6c65b1eac4e186d0ae55127f30` | +| Canonical demo | `KAC_OUTPUT_DIR=demo KAC_SITE_URL=https://knowledge-as-code.com/demo/ KAC_REPO_URL=https://github.com/snapsynapse/knowledge-as-code-template node scripts/build.js` | 22/22 sitemap routes | Pass | `d8cbce799fe5ec0b90def3a6b127d6b034b3c61fe2f479ad480a5f61dafef2fc` | + +Each final result contains zero confirmed major findings, zero nonblocking findings, zero unknown-impact violations, zero blocking or advisory incomplete candidates, and zero scan errors. The result files are local execution evidence and are identified here by digest; they are not shipped as product artifacts. + +The initializer golden path passed after the source changes. It continues to generate the supported fork structure and derives `.github/workflows/pages.yml` without moving the canonical repository's root Pages source. The generator's Node.js 18 and 20 compatibility matrix also remains in place; Node.js 22 is specific to the accessibility job. + +## Findings and repairs + +The initial template and demo scans had no confirmed violations, but each was inconclusive with 380 serious incomplete candidates across all 22 routes. A body gradient prevented deterministic contrast evaluation for 296 color candidates and 84 links. The root landing had one confirmed serious link distinction violation on the `PUBLISH-MCP.md` link plus 14 serious incomplete candidates involving a roleless labeled wordmark, decorative punctuation, and gradient-backed text. + +Repairs were made in the owning sources and regenerated into both output trees: + +- The generated site uses the existing dark or light background token as a solid page background so text contrast can be evaluated deterministically. +- Prose, list, breadcrumb, and footer links are visibly underlined. +- Timeline rails and markers are real decorative elements, relationship arrows are expressed as the words “maps to,” matrix counts have descriptive accessible names, and empty matrix cells retain “Not covered” text for assistive technology. +- The root wordmark has an image role, decorative separators and file arrows were removed, the confirmed link is underlined, and promotional statistics retain real text on a solid backing over the decorative gradient. +- The generated skip link and back-to-top control choose black or white foreground text from each configured accent color. + +No axe rule was disabled, no route was skipped, and no baseline was created or refreshed. + +## Theme checks + +Targeted axe checks covered the root landing under light and dark `prefers-color-scheme` emulation. The generated template and canonical demo index, matrix, and timeline pages were checked after applying and settling the same `light-mode` class changed by their theme button, and again in the default dark state. All 14 page-and-theme cases returned zero violations and zero incomplete candidates. + +The root highlighted heading text measured 17.40:1 against the light page background and 16.34:1 against the light highlight; the dark equivalents were 16.62:1 and 13.69:1. The generated back-to-top glyph measured 7.29:1 in light mode and 10.48:1 in dark mode. Its control boundary measured 6.70:1 and 8.51:1 respectively. + +This automated result covers only the routes, themes, and rules described here. It does not establish complete WCAG conformance or replace manual testing. diff --git a/demo/404.html b/demo/404.html index 85db421..933cf38 100644 --- a/demo/404.html +++ b/demo/404.html @@ -28,6 +28,8 @@ :is(html, body).light-mode .status-badge.deprecated { background: #455a64; color: #fff; } :root { --accent: #4fc3f7; } :is(html, body).light-mode { --accent: #0055aa; } +.skip-link, .back-to-top { color: #000; } +:is(html, body).light-mode .skip-link, :is(html, body).light-mode .back-to-top { color: #fff; }