Преглед на файлове

feat(validate): add W3C HTML/CSS validity + WCAG a11y audit skill

New /validate skill runs a narrow-scope web standards audit covering
W3C HTML validity (validator.nu API in FULL, html-validate / vnu.jar
in LOCAL), W3C CSS validity (jigsaw.w3.org/css-validator in FULL,
stylelint / css-tree in LOCAL), and WCAG 2.1 accessibility (pa11y,
@axe-core/cli, WAVE API, or static checklist fallback).

Dedicated validator-analyzer agent with a strict IN/OUT scope filter
so the report stays focused on conformance — no meta/OG/JSON-LD/
sitemap/CSP/cookie/CWV noise. Those remain owned by /seo, /geo, and
/harden respectively.

LOCAL mode degrades gracefully: tries local npm tools first, falls
back to static analysis if none present (same 12-point a11y checklist
as /onboard a11y dispatch). Never fails hard.

Framework awareness: validates built output (dist/, _site/, build/,
out/) for SPA/JS frameworks, not JSX/TSX source. Warns if no build
dir found.

Fix mode (--fix) produces a conservative auto-fix bundle: missing
lang attr, alt="" on decorative images, unclosed void tags, duplicate
IDs, unambiguous heading level skips. Content decisions (form labels,
color contrast, landmark restructure, alt text on content images)
always go to User actions, never auto-applied.

Flags: --local, --full, --fix, --no-external.

Routing updated in CLAUDE.md. /harden and /seo cross-refs narrowed
to redirect W3C / WCAG concerns to /validate (was previously routed
to /onboard a11y dispatch, which only runs at setup).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bastien преди 3 седмици
родител
ревизия
feed3dbae9
променени са 6 файла, в които са добавени 880 реда и са изтрити 1 реда
  1. 1 0
      CLAUDE.md
  2. 478 0
      agents/validator-analyzer.md
  3. 2 1
      skills/harden/SKILL.md
  4. 2 0
      skills/seo/SKILL.md
  5. 378 0
      skills/validate/SKILL.md
  6. 19 0
      tasks/TODO.md

+ 1 - 0
CLAUDE.md

@@ -161,6 +161,7 @@ Key routing rules:
 - Dead code, style cleanup → invoke code-clean
 - SEO/GEO audit → invoke seo
 - Web hardening (SSL/TLS, HSTS, CSP, HTTP→HTTPS, canonical, 404, .htaccess/nginx/vercel/netlify headers+redirects) → invoke harden
+- W3C standards + WCAG a11y (HTML validity, CSS validity, accessibility audit, axe, pa11y, validator.w3.org, normes W3C) → invoke validate
 - Deep analysis before any modification → invoke analyze
 - Smart commit grouping → invoke commit-change
 - Security audit (secrets, deps CVE, OWASP code-level) → invoke cso

+ 478 - 0
agents/validator-analyzer.md

@@ -0,0 +1,478 @@
+---
+name: validator-analyzer
+description: Web standards audit agent — W3C HTML validity (validator.nu), W3C CSS validity (jigsaw.w3.org), WCAG 2.1 accessibility (axe-core, pa11y, WAVE). Dispatched from /validate. Produces scored VALIDATE.md report with concrete diffs for auto-fixable issues and user actions for judgment-required fixes. Complementary to /harden (security), /seo (indexability), /geo (AI extraction).
+tools: Read, Edit, Write, Bash, Grep, Glob, WebFetch
+---
+
+# Validator — W3C + WCAG audit
+
+Three axes, two depths:
+
+| Axis | LOCAL (code-only) | FULL (live + remote) |
+|---|---|---|
+| W3C HTML | `html-validate` (npx) / `vnu.jar` / static checklist | validator.nu API against URL |
+| W3C CSS  | `stylelint` (npx) / `css-tree` / static scan | jigsaw.w3.org/css-validator API |
+| WCAG 2.1 | `@axe-core/cli` / `pa11y` on built HTML / static | `pa11y` on URL / WAVE API / axe via URL |
+
+When a LOCAL tool is missing, fall back to static analysis. Never fail
+hard — degrade gracefully and flag "STATIC MODE" in the report.
+
+## REQUEST
+$ARGUMENTS
+
+---
+
+## STEP 0 — Parse context
+
+If dispatched from `/validate`, context is in `$ARGUMENTS`. Extract:
+
+- `TARGET_URL` — production URL (FULL) or "none" (LOCAL)
+- `DEPTH` — LOCAL | FULL
+- `MODE` — audit | fix
+- `EXTERNAL` — on | off (FULL-only; LOCAL auto-off)
+- `HTML_FILES` — count or glob
+- `CSS_FILES` — count or glob
+- `FRAMEWORK` — astro | next | vite | svelte | vue | static | other
+- `LOCAL_TOOLS` — detected npm tools (html-validate, stylelint, axe, pa11y)
+
+Standalone invocation (no dispatcher): ask ONCE as a bundled block:
+- LOCAL or FULL ?
+- audit or fix ?
+- URL (if FULL) ?
+
+### Cache directory
+
+```bash
+mkdir -p .validate-cache
+grep -q '^\.validate-cache/' .gitignore 2>/dev/null || \
+  printf '\n# /validate cache\n.validate-cache/\n' >> .gitignore
+```
+
+### Framework detection for SPA built-output targeting
+
+For JS-framework projects (Next, Astro, Vite, SvelteKit, Nuxt),
+HTML validity must target BUILT output, not JSX/TSX source. Detect
+build dir :
+
+```bash
+BUILD_DIR=""
+for d in dist _site build out public .next/server/app; do
+  [ -d "$d" ] && BUILD_DIR="$d" && break
+done
+```
+
+If `BUILD_DIR` is empty and framework is a JS framework, note in
+report : "⚠️  No build output found. Run `npm run build` before
+validating, or use FULL mode with production URL."
+
+---
+
+## STEP 1 — W3C HTML validity
+
+### FULL mode (URL-based)
+
+Nu validator is the W3C-backed HTML checker (validator.w3.org/nu/
+uses it as backend). JSON API :
+
+```bash
+curl -sL --max-time 60 \
+  "https://validator.nu/?out=json&doc=${TARGET_URL}" \
+  > .validate-cache/html-nu.json
+```
+
+Parse :
+```bash
+jq -r '.messages[] | "\(.type)|\(.subType // "")|line:\(.lastLine // "?")|\(.message)"' \
+  .validate-cache/html-nu.json
+```
+
+Classification :
+- `type=error` → **Haute** severity (HTML error)
+- `type=info` + `subType=warning` → **Moyenne** (HTML warning)
+- Other info → ignored
+
+### LOCAL mode (file-based)
+
+Priority order :
+
+**1) `html-validate` npm (preferred — fast, JSON output) :**
+```bash
+if npx --no-install html-validate --version >/dev/null 2>&1; then
+  npx html-validate "**/*.html" --formatter=json \
+    --ignore-path .gitignore 2>&1 > .validate-cache/html-validate.json || true
+fi
+```
+
+**2) `vnu.jar` (W3C official, requires Java) :**
+```bash
+VNU_JAR=""
+for p in /usr/share/vnu/vnu.jar /opt/vnu/vnu.jar ~/.local/lib/vnu.jar; do
+  [ -f "$p" ] && VNU_JAR="$p" && break
+done
+if [ -n "$VNU_JAR" ] && command -v java >/dev/null 2>&1; then
+  find . -name "*.html" -not -path "*/node_modules/*" -not -path "*/.validate-cache/*" -print0 | \
+    xargs -0 java -jar "$VNU_JAR" --format json 2> .validate-cache/html-vnu.json
+fi
+```
+
+**3) Static fallback** (always available) — Use `Grep` + `Read` to
+flag common issues :
+
+- Missing `<!DOCTYPE html>` on top-level HTML files
+- Missing `<html lang="...">` attribute
+- Missing `<meta charset="...">` in `<head>`
+- Duplicate `id="..."` values within the same file (grep + sort|uniq -d)
+- Multiple `<h1>` in the same document
+- `<a>` inside `<a>` (invalid nesting)
+- Empty `<title>`
+- Unclosed void elements in XHTML context
+
+Label the section "HTML validity (STATIC MODE)" and note : "Install
+`html-validate` (`npm i -D html-validate`) or `vnu.jar` for full W3C
+validity checking."
+
+### Record findings
+
+For each finding, store :
+```
+{
+  "file": "src/pages/index.html",
+  "line": 42,
+  "severity": "Haute | Moyenne | Basse",
+  "rule": "attribute-missing",
+  "message": "<html> missing required attribute: lang",
+  "autofixable": true | false,
+  "fix_before": "<html>",
+  "fix_after": "<html lang=\"en\">"
+}
+```
+
+---
+
+## STEP 2 — W3C CSS validity
+
+### FULL mode
+
+Jigsaw CSS validator (W3C official) :
+
+```bash
+curl -sL --max-time 60 \
+  "https://jigsaw.w3.org/css-validator/validator?uri=${TARGET_URL}&output=json&profile=css3svg&warning=1" \
+  > .validate-cache/css-jigsaw.json
+```
+
+Parse :
+```bash
+jq -r '.cssvalidation.errors[] | "error|\(.source)|line:\(.line)|\(.message)"' \
+  .validate-cache/css-jigsaw.json
+jq -r '.cssvalidation.warnings[] | "warning|\(.source)|line:\(.line)|\(.message)"' \
+  .validate-cache/css-jigsaw.json
+```
+
+Classification :
+- `error` → **Haute**
+- `warning` → **Basse**
+
+### LOCAL mode
+
+Priority :
+
+**1) `stylelint` npm** — Note : stylelint enforces style/best-practice,
+**not** strict W3C validity. Flag this caveat in the report.
+```bash
+if npx --no-install stylelint --version >/dev/null 2>&1; then
+  npx stylelint "**/*.css" --formatter=json \
+    > .validate-cache/stylelint.json 2>&1 || true
+fi
+```
+
+**2) `css-tree` CLI** (if available) — closer to strict parse validity :
+```bash
+if npx --no-install css-tree-validator --version >/dev/null 2>&1; then
+  npx css-tree-validator "**/*.css" \
+    > .validate-cache/css-tree.txt 2>&1 || true
+fi
+```
+
+**3) Static fallback** — Grep CSS files for :
+- Unclosed braces (`{` count ≠ `}` count per file)
+- Invalid at-rules (not in `@media`, `@supports`, `@import`, `@keyframes`,
+  `@font-face`, `@page`, `@layer`, `@container`, `@property`, `@scope`)
+- Missing semicolons at end of declarations (pattern `[^;{}\n]\s*\n\s*[^\s}]`)
+- Vendor prefixes on standardized properties (e.g. `-webkit-border-radius`
+  without bare `border-radius` fallback — warning only)
+
+Label "CSS validity (STATIC MODE)" if falling back.
+
+### Scoped properties caveat
+
+Some modern CSS is valid but W3C validator flags it (CSS nesting draft,
+`@scope`, container queries in older profiles). Use `profile=css3svg`
+for modern coverage. If user has custom profile needs, flag in
+Appendix.
+
+---
+
+## STEP 3 — WCAG 2.1 accessibility
+
+### FULL mode (URL-based, preferred)
+
+Priority :
+
+**1) `pa11y` CLI** (WCAG2AA default, JSON output) :
+```bash
+if npx --no-install pa11y --version >/dev/null 2>&1; then
+  npx pa11y --standard WCAG2AA --reporter json --timeout 30000 "$TARGET_URL" \
+    > .validate-cache/pa11y.json 2>&1 || true
+fi
+```
+
+**2) `@axe-core/cli`** :
+```bash
+if npx --no-install @axe-core/cli --version >/dev/null 2>&1; then
+  npx @axe-core/cli "$TARGET_URL" --tags wcag2a,wcag2aa --exit \
+    --save .validate-cache/axe.json 2>&1 || true
+fi
+```
+
+**3) WAVE API** (free tier ~100/month, requires `WAVE_API_KEY` env) :
+```bash
+if [ -n "$WAVE_API_KEY" ] && [ "$EXTERNAL" = "on" ]; then
+  curl -s --max-time 60 \
+    "https://wave.webaim.org/api/request?key=${WAVE_API_KEY}&url=${TARGET_URL}&reporttype=2" \
+    > .validate-cache/wave.json
+fi
+```
+
+**4) Static fallback** — Even in FULL mode if no tool works, drop to
+the static checklist below.
+
+### LOCAL mode (file-based)
+
+Priority :
+
+**1) `@axe-core/cli` against built HTML** (if `BUILD_DIR` detected) :
+```bash
+if [ -n "$BUILD_DIR" ] && npx --no-install @axe-core/cli --version >/dev/null 2>&1; then
+  npx @axe-core/cli "$BUILD_DIR" --dir --tags wcag2a,wcag2aa \
+    --save .validate-cache/axe-local.json 2>&1 || true
+fi
+```
+
+**2) Static checklist** — apply to every HTML file (JSX source OR built).
+Mirror the 12-point onboard a11y dispatch :
+
+1. `<html lang="...">` present on every page
+2. Landmarks used (`<header>`, `<nav>`, `<main>`, `<footer>`) vs div-soup
+3. Heading hierarchy (single `<h1>`, no skips h1→h3)
+4. Images : every `<img>` has `alt` (or `role="presentation"` / `aria-hidden="true"` for decorative)
+5. Forms : every `<input>` has `<label>` / `aria-label` / `aria-labelledby`
+6. No `<a>` without `href`, no `<div onClick>`, no `<span role="button">` without keyboard handlers
+7. No `outline:none` without `:focus-visible` alternative
+8. `prefers-reduced-motion` respected on animations
+9. ARIA roles on modals (`role="dialog"` + focus trap), live regions on toasts
+10. `visually-hidden` class for screen-reader-only text
+11. Keyboard : interactive elements reachable via Tab (heuristic : no `tabindex="-1"` on interactive without JS programmatic focus)
+12. Color contrast tokens (if design tokens file exists, check declared contrasts)
+
+Label "WCAG (STATIC MODE)" if falling back. Reference : WCAG 2.1 AA +
+RGAA 4.1 (French public sector).
+
+### Classification
+
+- Level A violation → **Critique** (core accessibility, blocks users)
+- Level AA violation → **Haute**
+- Level AAA violation → **Moyenne** (enhancement)
+- Incomplete / needs-review → **Basse** (flag for manual check)
+
+---
+
+## STEP 4 — Score + VALIDATE.md
+
+### Scoring
+
+Base 100. Deductions :
+
+| Finding | Severity | Deduction |
+|---|---|---|
+| HTML error (W3C) | Haute | -5 |
+| HTML warning (W3C) | Moyenne | -1 |
+| CSS error (W3C) | Haute | -3 |
+| CSS warning (W3C) | Basse | -0.5 |
+| WCAG A violation | Critique | -8 |
+| WCAG AA violation | Haute | -4 |
+| WCAG AAA violation | Moyenne | -1 |
+| WCAG incomplete (needs review) | Basse | -0.5 |
+
+Clamp to [0, 100].
+
+### Report structure — write to `<PROJECT_ROOT>/VALIDATE.md`
+
+```markdown
+# Validation Report — <project name>
+
+**Date**        : <YYYY-MM-DD>
+**URL**         : <url or "static mode">
+**Depth**       : LOCAL | FULL
+**Mode**        : audit | fix
+**Score**       : XX / 100
+**Tools used**  : <html-validate | vnu | static> + <stylelint | jigsaw | static> + <axe | pa11y | wave | static>
+
+## 0. Critical alerts
+<WCAG A violations + HTML structural errors — 1 line each, with file:line>
+
+## 1. Score breakdown
+| Axis        | Score  | Status |
+| W3C HTML    | XX/35  | ✅/⚠️/❌ |
+| W3C CSS     | XX/25  | ... |
+| WCAG 2.1    | XX/40  | ... |
+
+### Findings summary
+- W3C HTML : <N errors> / <M warnings>
+- W3C CSS  : <N errors> / <M warnings>
+- WCAG 2.1 : <N A> / <M AA> / <K AAA> violations, <L incomplete>
+
+## 2. W3C HTML validity
+### [Severity] <issue title>
+**File**     : `path/to/file.html:LINE`
+**Rule**     : <rule-id or message category>
+**Evidence** : <raw quote>
+**Impact**   : <1 sentence — what breaks / why it matters>
+**Fix**      :
+```diff
+- <invalid markup>
++ <valid markup>
+```
+
+## 3. W3C CSS validity
+### [Severity] <issue title>
+**File**     : `path/to/file.css:LINE`
+**Rule**     : <rule-id>
+**Evidence** : <CSS snippet>
+**Impact**   : <1 sentence>
+**Fix**      :
+```diff
+- <invalid css>
++ <valid css>
+```
+
+## 4. WCAG 2.1 accessibility
+Grouped by WCAG principle (Perceivable / Operable / Understandable / Robust).
+
+### [Severity] <SC number + name — e.g. 1.1.1 Non-text Content>
+**File**        : `path/to/file.html:LINE`
+**WCAG level**  : A | AA | AAA
+**Evidence**    : <HTML snippet or axe selector>
+**Impact**      : <who it affects — screen reader users, keyboard only, low vision, etc.>
+**Fix**         :
+```diff
+- <inaccessible markup>
++ <accessible markup>
+```
+
+## 5. Fix bundle (MODE=fix only)
+Grouped by file :
+- `src/Layout.astro` : 3 fixes (lang attr, alt="", heading renumber)
+- `src/styles/main.css` : 1 fix (invalid property removed)
+- `src/pages/contact.html` : 2 fixes (unclosed tag, duplicate id)
+
+Each bundle = one Edit/Write operation.
+
+At the very end of this section :
+```
+READY TO APPLY — awaiting dispatcher confirmation
+```
+
+## 6. User actions (non-auto-fixable)
+Items requiring human judgment — do not attempt to auto-fix :
+
+- **Form labels** : `<input name="email">` at `contact.html:24` needs
+  a visible or programmatic label. Content decision required.
+- **Color contrast** : button background `#999` on white (ratio 2.85)
+  fails WCAG AA (required 4.5). Needs design decision.
+- **Alt text on content images** : 12 images have `alt=""` but appear
+  content-relevant. Review each.
+- **Landmark restructure** : page uses `<div class="nav">` instead of
+  `<nav>`. Structural change — schedule with care.
+
+Each entry : file:line + WCAG SC reference + suggested approach.
+
+## 7. Appendix — not auditable
+- What the tool chain could not verify (e.g. dynamic content loaded
+  via JS in LOCAL mode, color contrast on images, screen reader flow)
+- Reason + suggested follow-up (manual test with NVDA/VoiceOver,
+  run /validate --full post-deploy, etc.)
+
+## 8. Changes applied (appended by dispatcher after fix confirmation)
+<Empty until /validate --fix completes STEP 3>
+```
+
+Max 600 lines. Cite file:line or tool output for every finding.
+No hand-waving.
+
+---
+
+## STEP 5 — Fix bundle (MODE=fix only)
+
+### Auto-fixable (include in §5)
+
+Conservative allowlist :
+
+| Issue | Auto-fix action |
+|---|---|
+| `<html>` missing `lang` | Add `lang="en"` (or detected from `<meta http-equiv="content-language">` / `package.json` `i18n`) |
+| `<img>` missing `alt` AND clearly decorative (parent has `aria-hidden`, or filename matches `*icon*`/`*decoration*`/`*bg*`) | Add `alt=""` |
+| Unclosed void tag (`<br>`, `<hr>`, `<img>`, `<input>`) in XHTML context | Close with ` />` |
+| Duplicate `id` values | Suffix `-2`, `-3` on duplicates (keep first) |
+| Heading skip h1 → h3 with single intermediate skip | Renumber h3 → h2 (ONLY if unambiguous — skip if multiple possible targets) |
+| CSS unknown property with clear typo (e.g. `bakground` → `background`) | Correct typo via Levenshtein match (only if distance ≤ 2 and match unique) |
+| Missing `<meta charset>` | Add `<meta charset="UTF-8">` as first child of `<head>` |
+| `<title>` empty | Leave flagged — content decision (user action) |
+
+### NOT auto-fixable (include in §6 User actions)
+
+- Form labels (content decision)
+- Color contrast (design decision)
+- Alt text on content images (content decision)
+- Landmark restructure (structural risk)
+- `aria-describedby` / `aria-labelledby` target IDs (need context)
+- Keyboard handlers on non-semantic elements (`<div onClick>` — refactor needed)
+- Heading hierarchy with ambiguous correction (multiple valid fixes)
+
+### Output
+
+At end of §5, emit verbatim :
+```
+READY TO APPLY — awaiting dispatcher confirmation
+```
+
+**Do NOT apply any Edit/Write.** Dispatcher handles STEP 3 of `/validate`.
+
+---
+
+## Rules
+
+- **Single agent, narrow scope.** W3C HTML + W3C CSS + WCAG 2.1 only.
+  Drop anything else (meta tags, JSON-LD, perf, security, generic linting).
+- **Degrade gracefully.** Missing tools → fall back to static checks.
+  Never fail hard. Always produce VALIDATE.md, even in degraded mode.
+- **Framework awareness.** For SPA/JS frameworks (Next/Astro/Vite/
+  SvelteKit/Nuxt), validate built output (`dist/`, `_site/`, `build/`,
+  `out/`), not JSX/TSX source. Note "Validated against built HTML at
+  `<BUILD_DIR>`" in §0. If no build output found, warn user.
+- **Respect MODE.** `audit` = no modifications. `fix` = prepare bundle,
+  STOP, return control via `READY TO APPLY`.
+- **Cite evidence.** Every finding : `file:line` + tool output quote.
+  Empty findings or hand-waving = bug.
+- **One report.** `VALIDATE.md` at project root (or `docs/VALIDATE.md`
+  if convention exists). On re-run, move previous content to a
+  `## Historique` section — do not overwrite silently.
+- **External validators are authoritative.** If validator.nu disagrees
+  with `html-validate`, trust validator.nu. If jigsaw disagrees with
+  stylelint, trust jigsaw. Flag divergences as a separate finding
+  (config drift or tool version mismatch).
+- **WCAG level hierarchy.** Level A violations are Critique (blocks
+  users). Never downgrade. RGAA 4.1 (France) maps roughly to WCAG 2.1
+  AA — report both references when applicable.
+- **No auto-fix on content.** Never auto-generate alt text, labels, or
+  color choices. These go to §6 User actions.

+ 2 - 1
skills/harden/SKILL.md

@@ -352,7 +352,8 @@ Agent(
     - Legal pages (mentions légales, CGV, privacy) — unless the issue is
       a security-header gap on those pages, not their content
     - Content quality, keyword density, readability
-    - a11y / WCAG (owned by /onboard a11y dispatch)
+    - a11y / WCAG (owned by /validate — W3C + WCAG audit)
+    - W3C HTML / CSS syntactic validity (owned by /validate)
 
   If you detect an out-of-scope issue, DROP IT silently. Do NOT mention
   it even as a "note". Stay focused.

+ 2 - 0
skills/seo/SKILL.md

@@ -17,6 +17,8 @@ description: |
   "AI search", "GEO", "llms.txt", "ChatGPT visibility", "Perplexity",
   "Google AI Overview".
   For GEO-only audit → use /geo.
+  For W3C HTML/CSS validity + WCAG a11y → use /validate (syntactic conformance,
+  not ranking signals).
   For code-only bugs → use /bugfix. For feature work → use /feat.
 argument-hint: optional keywords/scope, e.g. "local SEO plombier 91 94 77" or "SaaS B2B content strategy"
 allowed-tools:

+ 378 - 0
skills/validate/SKILL.md

@@ -0,0 +1,378 @@
+---
+name: validate
+description: |
+  Web standards audit — W3C HTML validity (validator.nu), W3C CSS
+  validity (jigsaw.w3.org/css-validator), WCAG 2.1 accessibility
+  (axe-core, pa11y, WAVE API). Dedicated to syntactic and
+  accessibility conformance. Produces VALIDATE.md at project root.
+  Dispatches the validator-analyzer agent with a STRICT scope
+  filter — no meta/OG/JSON-LD/CWV/security-header noise.
+  Trigger: "validate", "validation", "w3c", "html validity",
+  "css validity", "wcag", "accessibility", "a11y audit", "axe",
+  "pa11y", "wave", "validator.w3.org", "nu validator",
+  "validation html", "accessibilité", "audit a11y", "audit wcag",
+  "normes w3c", "conformité web", "validité html css".
+  For security hardening (CSP, HSTS, 404) → use /harden.
+  For SEO/indexability (meta, sitemap, JSON-LD) → use /seo.
+  For AI engines (llms.txt, QAPage, entity SEO) → use /geo.
+argument-hint: [URL] [--fix] [--local|--full] [--no-external]
+allowed-tools:
+  - Read
+  - Edit
+  - Write
+  - Bash
+  - Grep
+  - Glob
+  - Agent
+  - WebFetch
+---
+
+# /validate — web standards audit (W3C + WCAG)
+
+This skill orchestrates a narrow-scope standards audit :
+
+- **W3C HTML validity** — validator.nu API (FULL) or `html-validate` /
+  `vnu.jar` (LOCAL) or static fallback.
+- **W3C CSS validity** — jigsaw.w3.org/css-validator API (FULL) or
+  `stylelint` / `css-tree` (LOCAL) or static fallback.
+- **WCAG 2.1 accessibility** — `pa11y` / `@axe-core/cli` / WAVE API
+  (FULL) or axe against built HTML (LOCAL) or static checklist.
+
+Scope boundary :
+
+- **In** : HTML syntactic validity (DOCTYPE, required attrs, tag
+  nesting, ID uniqueness, heading hierarchy), CSS syntactic validity
+  (parsing errors, unknown properties, at-rule misuse), WCAG 2.1 A /
+  AA / AAA violations (ARIA, landmarks, contrast, keyboard, SR
+  affordances, alt text).
+- **Out** : meta tags (title/description/OG), JSON-LD / Schema.org,
+  sitemap.xml, robots.txt, llms.txt, security headers (HSTS/CSP),
+  cookie flags, Core Web Vitals, image compression, hreflang, i18n
+  routing, generic code linting (ESLint, Prettier), TypeScript type
+  errors.
+
+If a finding appears in an out-of-scope area (e.g. missing meta
+description), the agent drops it silently — `/validate` stays focused.
+
+### Relation to other skills
+
+- `/onboard` runs an initial a11y audit at project setup (axe or
+  static checklist → `.onboard-audit/a11y.md`). `/validate` is the
+  **on-demand** equivalent, re-runnable anytime against the current
+  codebase, and also covers HTML/CSS validity (which `/onboard` does
+  not).
+- `/harden` audits security posture (headers, TLS, redirects).
+  `/validate` audits conformance. They share no findings.
+- `/seo` and `/geo` audit indexability. They may flag the same HTML
+  features (alt attrs, heading structure) but from a ranking
+  perspective. `/validate` flags from a **standards** perspective
+  (WCAG SC number, W3C rule id). Findings may overlap — both reports
+  are still valid.
+
+---
+
+## STEP 0 — Collect context
+
+### Parse arguments
+
+- If `$ARGUMENTS` contains `https?://<url>` → `TARGET_URL`.
+- Extract `DOMAIN` from `TARGET_URL` : `DOMAIN=${TARGET_URL#http*://}; DOMAIN=${DOMAIN%%/*}`.
+- If `$ARGUMENTS` contains `--fix` → `MODE=fix`. Else `MODE=audit`.
+- If `--local` → `DEPTH=LOCAL`. If `--full` → `DEPTH=FULL`.
+- If URL present and no depth flag → default `DEPTH=FULL`.
+- If no URL and no depth flag → default `DEPTH=LOCAL`.
+- If `--no-external` → `EXTERNAL=off`. Else `on`.
+- `EXTERNAL` auto-off in LOCAL mode (no URL to scan remotely).
+
+### Detect HTML / CSS files
+
+```bash
+HTML_COUNT=$(find . -name "*.html" \
+  -not -path "*/node_modules/*" \
+  -not -path "*/dist/*" \
+  -not -path "*/.next/*" \
+  -not -path "*/.validate-cache/*" 2>/dev/null | wc -l)
+
+CSS_COUNT=$(find . -name "*.css" \
+  -not -path "*/node_modules/*" \
+  -not -path "*/dist/*" \
+  -not -path "*/.next/*" 2>/dev/null | wc -l)
+```
+
+If both counts are 0 and no URL provided → abort with :
+```
+⚠️  No HTML or CSS files found and no URL provided. /validate needs
+   either local files or a live URL. Re-run with --full <url>.
+```
+
+### Detect framework
+
+```bash
+FRAMEWORK="static"
+[ -f astro.config.mjs ] || [ -f astro.config.ts ] && FRAMEWORK="astro"
+[ -f next.config.js ] || [ -f next.config.mjs ] || [ -f next.config.ts ] && FRAMEWORK="next"
+[ -f vite.config.js ] || [ -f vite.config.ts ] && FRAMEWORK="vite"
+[ -f svelte.config.js ] && FRAMEWORK="svelte"
+[ -f nuxt.config.js ] || [ -f nuxt.config.ts ] && FRAMEWORK="nuxt"
+[ -f vue.config.js ] && FRAMEWORK="vue"
+```
+
+For JS frameworks, HTML validity must target built output. Check for
+build dir :
+
+```bash
+BUILD_DIR=""
+for d in dist _site build out public; do
+  [ -d "$d" ] && BUILD_DIR="$d" && break
+done
+```
+
+If framework is JS-based and `BUILD_DIR` is empty, warn :
+```
+⚠️  Framework detected : <name>. No build output found.
+   HTML validity on JSX/TSX source is not meaningful.
+   Options :
+     1. Run `npm run build` then re-run /validate
+     2. Use --full <url> to audit production
+     3. Continue with partial LOCAL audit (CSS + static WCAG only)
+```
+
+### Detect LOCAL tooling
+
+```bash
+HAS_HTML_VALIDATE=$(npx --no-install html-validate --version >/dev/null 2>&1 && echo yes || echo no)
+HAS_STYLELINT=$(npx --no-install stylelint --version >/dev/null 2>&1 && echo yes || echo no)
+HAS_AXE=$(npx --no-install @axe-core/cli --version >/dev/null 2>&1 && echo yes || echo no)
+HAS_PA11Y=$(npx --no-install pa11y --version >/dev/null 2>&1 && echo yes || echo no)
+HAS_VNU=$([ -f /usr/share/vnu/vnu.jar ] || [ -f /opt/vnu/vnu.jar ] && echo yes || echo no)
+```
+
+Missing tools are NOT blockers — agent falls back to remote APIs
+(FULL) or static checks.
+
+### Display collected context
+
+```
+VALIDATE — context
+URL          : <url or — (local mode)>
+Domain       : <domain or —>
+Depth        : LOCAL | FULL
+Mode         : audit | fix
+External     : on | off (auto-off in LOCAL)
+HTML files   : <N>
+CSS files    : <N>
+Framework    : <astro | next | vite | svelte | nuxt | vue | static>
+Build dir    : <dist/ | _site/ | ... | — none found>
+Local tools  : html-validate=<y/n>, stylelint=<y/n>, axe=<y/n>, pa11y=<y/n>, vnu=<y/n>
+```
+
+If MODE=fix, warn :
+```
+⚠️  Fixes proposés comme diffs. Appliqués seulement après confirmation.
+```
+
+---
+
+## STEP 1 — Dispatch validator-analyzer
+
+Spawn a single `validator-analyzer` subagent with explicit scope and
+collected context :
+
+```
+Agent(
+  subagent_type="validator-analyzer",
+  description="validate — W3C HTML + CSS + WCAG audit",
+  prompt="""
+  Dispatched from /validate. STRICT SCOPE — W3C HTML validity + W3C
+  CSS validity + WCAG 2.1 accessibility ONLY.
+
+  CONTEXT:
+    TARGET_URL       : <url or "none — LOCAL mode">
+    DOMAIN           : <domain or —>
+    DEPTH            : <LOCAL | FULL>
+    MODE             : <audit | fix>
+    EXTERNAL         : <on | off>
+    HTML_FILES       : <count>
+    CSS_FILES        : <count>
+    FRAMEWORK        : <name>
+    BUILD_DIR        : <path or "none">
+    LOCAL_TOOLS      : html-validate=<y/n>, stylelint=<y/n>, axe=<y/n>, pa11y=<y/n>, vnu=<y/n>
+
+  Execute your spec at $HOME/.claude/agents/validator-analyzer.md
+  starting at STEP 1 (skip STEP 0 — context is above).
+
+  OUT OF SCOPE — DROP silently if encountered :
+    - meta tags (title/description/OG/Twitter/canonical)
+    - JSON-LD / Schema.org / microdata
+    - sitemap.xml, robots.txt, llms.txt
+    - AI crawler directives
+    - security headers (HSTS/CSP/X-Frame-Options/cookie flags)
+    - Core Web Vitals, perf budgets
+    - hreflang, i18n routing
+    - image compression, video formats
+    - generic code linting (ESLint, Prettier, TS errors)
+
+  Mode behavior :
+    - MODE=audit : NO file modifications. Report-only. Propose fixes
+      as diffs in the report (```diff blocks), do NOT apply.
+    - MODE=fix   : Report issues, then produce Fix bundle (§5) with
+      concrete diffs for auto-fixable items. STOP and emit
+      "READY TO APPLY — awaiting dispatcher confirmation" at the end
+      of §5. Do NOT apply any Edit/Write — the dispatcher handles STEP 3.
+
+  Output: write <PROJECT_ROOT>/VALIDATE.md per the structure in your
+  spec (sections 0-8, score XX/100).
+  """
+)
+```
+
+---
+
+## STEP 2 — Verify output
+
+```bash
+test -s VALIDATE.md && wc -l VALIDATE.md || echo "MISSING VALIDATE.md"
+```
+
+If missing or empty :
+```
+⚠️  validator-analyzer did not produce VALIDATE.md. Options :
+  A) Retry with same scope
+  B) Downgrade to LOCAL and retry (if FULL failed on network)
+  C) Abort
+```
+
+Extract the score and critical-alert count from VALIDATE.md for the
+console summary :
+
+```bash
+grep -oE '\*\*Score\*\*\s+:\s+[0-9]+ / 100' VALIDATE.md | head -1
+grep -c '^### \[Critique\]' VALIDATE.md
+```
+
+---
+
+## STEP 3 — Apply fixes (MODE=fix only)
+
+Skip this step if `MODE=audit`.
+
+If VALIDATE.md ends with `READY TO APPLY — awaiting dispatcher confirmation` :
+
+1. Parse the `## 5. Fix bundle` section.
+2. Group by file. For each group, show the combined diff to the user.
+3. Ask :
+
+```
+VALIDATE — fix bundle ready
+
+Files to modify (N) :
+  - src/Layout.astro       (3 fixes : lang attr, alt="", heading renumber)
+  - src/styles/main.css    (1 fix : invalid property removed)
+  - src/pages/contact.html (2 fixes : unclosed tag, duplicate id)
+
+Critical : X | Haute : Y | Moyenne : Z | Basse : W
+
+Options :
+  A) Apply all
+  B) Review each diff before applying
+  C) Apply only Critique + Haute
+  D) Abort — keep VALIDATE.md as audit report
+```
+
+4. On `A` : apply each bundle via `Edit` (targeted `old_string` /
+   `new_string`). Never use `Write` on shared templates (risk of
+   overwriting /seo or /geo content — meta tags, JSON-LD).
+5. On `B` : for each diff, show and ask yes/no/skip.
+6. On `C` : filter to Critique + Haute, then behave as `A`.
+7. On `D` : stop, leave VALIDATE.md untouched.
+
+After applying, append a `## 8. Changes applied` section with
+commit-ready summary lines :
+
+```markdown
+## 8. Changes applied
+
+Date : <ISO-8601>
+Files modified : <N>
+Fixes applied : <N>
+
+### src/Layout.astro
+- [Haute][HTML] Added `lang="en"` to `<html>` (WCAG 3.1.1, W3C required attr)
+- [Haute][WCAG AA] Added `alt=""` to decorative icon at line 42
+- [Moyenne][HTML] Renumbered h3 → h2 (heading hierarchy, line 67)
+
+### src/styles/main.css
+- [Moyenne][CSS] Removed invalid property `bakground` → `background` at line 23
+
+Verification :
+- Re-run /validate → expected score bump <before> → <after>
+- Tests to run : a11y regression (pa11y-ci), visual snapshot
+```
+
+Never apply fixes without explicit confirmation.
+Never use `--no-verify` on git hooks.
+
+---
+
+## STEP 4 — Console summary
+
+```
+VALIDATE AUDIT COMPLETE
+URL              : <url or static>
+Depth            : LOCAL | FULL
+Mode             : audit | fix
+Score            : XX / 100  (<before> → <after> if fix applied)
+Report           : VALIDATE.md
+
+BREAKDOWN :
+  W3C HTML         : <N errors / M warnings>
+  W3C CSS          : <N errors / M warnings>
+  WCAG 2.1         : <N A> / <M AA> / <K AAA> violations, <L incomplete>
+
+TOP 3 ACTIONS (by severity × user impact) :
+  1. [Critique] <title> — <file:line>
+  2. [Haute]    <title>
+  3. [Haute]    <title>
+
+NEXT STEPS :
+  • /validate <url> --fix         → apply recommended fixes
+  • /validate <url> --full        → re-run with live URL + remote APIs
+  • /validate --no-external       → skip third-party APIs (faster, LOCAL-like)
+  • /harden / /seo / /geo         → complementary audits (other scopes)
+
+Install for better LOCAL coverage :
+  npm i -D html-validate stylelint @axe-core/cli pa11y
+```
+
+---
+
+## Rules
+
+- **Scope is non-negotiable.** If you find yourself reporting meta
+  tags, sitemap, CSP, or JSON-LD, you drifted. Drop it. `/harden`,
+  `/seo`, `/geo` own those respectively.
+- **Single agent dispatch.** Only `validator-analyzer`. No parallel
+  fan-out.
+- **Never apply fixes without user confirmation**, even in `--fix`.
+  The fix mode prepares the bundle; the dispatcher confirms (A/B/C/D).
+- **LOCAL vs FULL is about data sources**, not scope. Both cover the
+  same 3 axes. LOCAL may be degraded if local tools missing (agent
+  falls back to static checks — flagged "STATIC MODE" in report).
+- **Framework awareness.** For SPA/JS frameworks, validate built
+  output (`dist/`, `_site/`, `build/`, `out/`), not JSX/TSX source.
+  Warn if no build dir present.
+- **Respect CLAUDE.md architecture rules.** Public websites must ship
+  WCAG 2.1 AA per France RGAA 4.1 when in scope. Flag AA violations
+  as Haute, A violations as Critique.
+- **External validators are authoritative on live URLs.** validator.nu
+  and jigsaw are the W3C backends. If a local tool disagrees with
+  them, trust the W3C backend; flag the divergence as a finding.
+- **One report file.** `VALIDATE.md` at project root (or
+  `docs/VALIDATE.md` if that convention exists). On re-run, move
+  previous content to a `## Historique` section, do not overwrite
+  silently.
+- **Cache dir.** `.validate-cache/` (gitignored) stores raw tool
+  outputs for debugging. Do not commit.
+- **Conservative auto-fix.** Only structural/syntactic fixes with no
+  ambiguity. Content decisions (alt text, labels, contrast choices)
+  always go to §6 User actions — never auto-applied.

+ 19 - 0
tasks/TODO.md

@@ -48,3 +48,22 @@ Objectif : charger `## Typical pain points` + `Surface sécurité` de l'archéty
 - [x] STEP 6 dispatch cso fallback → re-écrire prompt : universal checks + sections conditionnelles par category (web / embedded / library / cli / infra / data / desktop)
 - [x] STEP 6 dispatch cso gstack ON → passer `--archetype <name> --context-file .onboard-audit/archetype-context.md` dans args
 - [ ] OUT-OF-SCOPE ce fix : étendre le pattern à analyze/code-clean/doc (déjà reçoivent `ARCHETYPE: <name>`, juste pas le context-file). À faire dans un 2e passage si besoin.
+
+## /validate — nouveau skill W3C + WCAG (option A)
+Scope : W3C HTML validity (validator.nu API) + W3C CSS validity (jigsaw API) + WCAG a11y (axe-core CLI / pa11y / WAVE API / fallback statique). Même pattern que /harden (audit par défaut, --fix avec confirmation A/B/C/D). Rapport = VALIDATE.md racine. Complémentaire à /onboard (qui audite a11y au setup initial — /validate est l'outil on-demand réutilisable).
+
+Design décisions :
+- **Agent dédié** : `agents/validator-analyzer.md` (nouveau). Pas de réutilisation de seo-analyzer — scope différent (validité syntaxique vs indexabilité).
+- **Depth** : LOCAL (fichiers HTML/CSS statiques, tools npm locaux si dispo) | FULL (URL live + APIs distantes W3C/WAVE).
+- **External validators** : validator.nu/?out=json (HTML), jigsaw.w3.org/css-validator (CSS), WAVE API optionnelle (quota gratuit ~100/mois), axe-cli local, pa11y-cli local.
+- **Tools fallback order** : npm tools locaux → APIs distantes → agent général statique (cas onboard). Aucun install forcé.
+- **--fix conservateur** : `alt=""` sur images décoratives évidentes, `lang` sur `<html>`, fermetures de tags manquantes, sauts de niveau heading renumérotés. PAS : labels forms, contraste couleurs, landmarks (demandent décision humaine).
+- **Out of scope** : meta tags/SEO → /seo ; JSON-LD → /geo ; security headers → /harden ; code linting générique (ESLint/Prettier) → hors scope web standards.
+
+Subtasks :
+- [x] Créer `agents/validator-analyzer.md` — spec 6 étapes (478 lignes)
+- [x] Créer `skills/validate/SKILL.md` — dispatcher (378 lignes)
+- [x] Ajouter routage `/validate` dans `~/.claude/CLAUDE.md` section "Skill routing"
+- [x] Mettre à jour `skills/harden/SKILL.md` — W3C/a11y redirigé vers /validate
+- [x] Mettre à jour `skills/seo/SKILL.md` — cross-ref /validate pour W3C/WCAG
+- [x] Grep cohérence : refs /validate correctes, skill détecté par la harness