@adia-ai/adia-ui-forge 0.1.3 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +4 -7
- package/CHANGELOG.md +35 -34
- package/README.md +35 -40
- package/agents/a2ui-engineer.md +22 -53
- package/agents/component-author.md +29 -0
- package/agents/framework-verifier.md +26 -0
- package/agents/release-engineer.md +21 -81
- package/agents/routing-corpus.json +293 -64
- package/bin/demo-postwrite-pattern-gate +94 -0
- package/bin/forge-lint +17 -2
- package/bin/sidecar-prewrite-guard +104 -0
- package/commands/deploy.md +9 -0
- package/commands/dogfood.md +10 -0
- package/commands/gen-review.md +9 -0
- package/commands/release.md +10 -0
- package/hooks/hooks.json +15 -0
- package/package.json +3 -6
- package/references/contracts/a2ui-mcp-surface.md +35 -0
- package/references/contracts/migration-guide-format.md +34 -0
- package/references/shared/content-trust.md +19 -74
- package/skills/adia-a2ui/SKILL.md +99 -0
- package/skills/{adia-ui-a2ui → adia-a2ui}/evals/routing-corpus.json +10 -10
- package/skills/adia-a2ui/references/anti-patterns.md +27 -0
- package/skills/adia-a2ui/references/chunk-authoring.md +71 -0
- package/skills/adia-a2ui/references/corpus-discipline.md +68 -0
- package/skills/adia-a2ui/references/eval-diagnostics.md +86 -0
- package/skills/adia-a2ui/references/format-extension-decisions.md +66 -0
- package/skills/adia-a2ui/references/leverage-rules.md +52 -0
- package/skills/adia-a2ui/references/mcp-pipeline-ops.md +83 -0
- package/skills/adia-a2ui/references/mcp-tool-reference.md +59 -0
- package/skills/adia-a2ui/references/pipeline-overview.md +115 -0
- package/skills/adia-a2ui/references/semantic-fail-lifting.md +74 -0
- package/skills/adia-a2ui/references/strategy-engines.md +109 -0
- package/skills/adia-a2ui/references/zettel-calibration.md +104 -0
- package/skills/adia-author/SKILL.md +112 -0
- package/skills/adia-author/evals/routing-corpus.json +222 -0
- package/skills/{adia-ui-authoring → adia-author}/references/anti-patterns.md +33 -4
- package/skills/{adia-ui-authoring → adia-author}/references/authoring-cycle.md +9 -11
- package/skills/adia-author/references/canonical-pattern-index.md +243 -0
- package/skills/{adia-ui-authoring → adia-author}/references/code-style.md +19 -21
- package/skills/adia-author/references/common-gotchas.md +129 -0
- package/skills/adia-author/references/composite-demo-protocol.md +271 -0
- package/skills/{adia-ui-authoring → adia-author}/references/css-patterns.md +19 -6
- package/skills/{adia-ui-authoring → adia-author}/references/lifecycle-patterns.md +1 -0
- package/skills/{adia-ui-authoring → adia-author}/references/llm-bridge.md +3 -5
- package/skills/{adia-ui-authoring → adia-author}/references/module-promotion.md +20 -38
- package/skills/{adia-ui-authoring → adia-author}/references/primitive-audit.md +2 -4
- package/skills/{adia-ui-authoring → adia-author}/references/shell-patterns.md +12 -18
- package/skills/{adia-ui-authoring → adia-author}/references/token-contract.md +5 -7
- package/skills/{adia-ui-authoring → adia-author}/references/worked-example.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/references/yaml-contract.md +3 -3
- package/skills/{adia-ui-authoring → adia-author}/scripts/build-canonical-pattern-index.mjs +3 -3
- package/skills/adia-deploy/SKILL.md +128 -0
- package/skills/{adia-ui-ops → adia-deploy}/references/deploy-playbooks.md +47 -30
- package/skills/adia-dogfood/SKILL.md +124 -0
- package/skills/adia-dogfood/references/admin-shell-anatomy.md +77 -0
- package/skills/adia-dogfood/references/app-shell-pitfalls.md +66 -0
- package/skills/adia-dogfood/references/card-anatomy-sweep.md +66 -0
- package/skills/adia-dogfood/references/html-attr-sweep.md +63 -0
- package/skills/adia-dogfood/references/native-leak-annotations.md +73 -0
- package/skills/adia-dogfood/references/visual-probe-triage.md +80 -0
- package/skills/{adia-ui-dogfood → adia-dogfood}/scripts/analyze.mjs +30 -14
- package/skills/adia-gen-review/SKILL.md +127 -0
- package/skills/adia-gen-review/references/corpus-html-patterns.md +279 -0
- package/skills/adia-gen-review/references/loop-protocol.md +216 -0
- package/skills/adia-gen-review/references/rubric-cosmetic.md +100 -0
- package/skills/adia-gen-review/references/rubric-decompose.md +101 -0
- package/skills/adia-gen-review/references/rubric-score.md +214 -0
- package/skills/{adia-ui-gen-review → adia-gen-review}/references/scores.schema.json +2 -2
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-coverage-audit.mjs +4 -4
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-decompose.mjs +10 -6
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/gen-review-status.mjs +5 -5
- package/skills/{adia-ui-gen-review → adia-gen-review}/scripts/validate-cycle-scores.mjs +5 -5
- package/skills/adia-llm-internals/SKILL.md +77 -0
- package/skills/{adia-ui-llm → adia-llm-internals}/references/adapter-contract.md +4 -2
- package/skills/{adia-ui-llm → adia-llm-internals}/references/add-a-provider.md +4 -5
- package/skills/{adia-ui-llm → adia-llm-internals}/references/bridge-facade.md +10 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/browser-proxy-boundary.md +1 -1
- package/skills/{adia-ui-llm → adia-llm-internals}/references/model-registry.md +5 -3
- package/skills/{adia-ui-llm → adia-llm-internals}/references/streaming-sse.md +2 -2
- package/skills/adia-release/SKILL.md +72 -0
- package/skills/adia-release/references/changelog-discipline.md +119 -0
- package/skills/adia-release/references/cut-procedure.md +247 -0
- package/skills/adia-release/references/gates-catalog.md +222 -0
- package/skills/adia-release/references/independent-package-release.md +52 -0
- package/skills/adia-release/references/migration-guide-authoring.md +86 -0
- package/skills/adia-release/references/notes-authoring.md +90 -0
- package/skills/adia-release/references/recovery-paths.md +106 -0
- package/skills/{adia-ui-release → adia-release}/scripts/bump.mjs +28 -3
- package/skills/{adia-ui-release → adia-release}/scripts/dispatch-publish.mjs +10 -7
- package/skills/{adia-ui-release → adia-release}/scripts/insert-stub.mjs +1 -1
- package/skills/{adia-ui-release → adia-release}/scripts/package-paths.mjs +6 -0
- package/skills/{adia-ui-release → adia-release}/scripts/promote-unreleased.mjs +1 -1
- package/skills/{adia-ui-release → adia-release}/scripts/release-pack.mjs +38 -31
- package/skills/{adia-ui-release → adia-release}/scripts/tag-lockstep.mjs +7 -3
- package/skills/adia-site-docs/SKILL.md +68 -0
- package/skills/adia-site-docs/evals/audit-report.md +30 -0
- package/skills/adia-site-docs/evals/routing-corpus.json +176 -0
- package/skills/adia-site-docs/intent.md +72 -0
- package/agents/README.md +0 -209
- package/agents/author.md +0 -56
- package/agents/repo-steward.md +0 -56
- package/agents/spec-architect.md +0 -53
- package/agents/tech-lead.md +0 -57
- package/agents/verifier.md +0 -55
- package/bin/lib/audit-axes.mjs +0 -555
- package/bin/lib/dry-run-irreversible.mjs +0 -236
- package/bin/lib/run-skill-evals.mjs +0 -487
- package/bin/lib/teach-router.mjs +0 -250
- package/commands/adia-forge-a2ui.md +0 -10
- package/commands/adia-forge-author.md +0 -10
- package/commands/adia-forge-dogfood.md +0 -8
- package/commands/adia-forge-llm.md +0 -8
- package/commands/adia-forge-orient.md +0 -10
- package/commands/adia-forge-release.md +0 -8
- package/commands/adia-forge-review.md +0 -10
- package/references/shared/pev-rationale.md +0 -64
- package/references/shared/skill-conventions.md +0 -133
- package/skills/adia-ui-a2ui/CHANGELOG.md +0 -32
- package/skills/adia-ui-a2ui/SKILL.md +0 -243
- package/skills/adia-ui-a2ui/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-a2ui/evals/teach-routing-cases.json +0 -100
- package/skills/adia-ui-a2ui/references/anti-patterns.md +0 -24
- package/skills/adia-ui-a2ui/references/chunk-authoring.md +0 -88
- package/skills/adia-ui-a2ui/references/corpus-discipline.md +0 -56
- package/skills/adia-ui-a2ui/references/eval-diagnostics.md +0 -127
- package/skills/adia-ui-a2ui/references/fragment-graph.md +0 -91
- package/skills/adia-ui-a2ui/references/mcp-pipeline-ops.md +0 -106
- package/skills/adia-ui-a2ui/references/mcp-tool-reference.md +0 -398
- package/skills/adia-ui-a2ui/references/pipeline-overview.md +0 -175
- package/skills/adia-ui-a2ui/references/semantic-fail-lifting.md +0 -120
- package/skills/adia-ui-a2ui/references/strategy-engines.md +0 -111
- package/skills/adia-ui-a2ui/references/teach-protocol.md +0 -220
- package/skills/adia-ui-a2ui/references/zettel-calibration.md +0 -93
- package/skills/adia-ui-a2ui/scripts/audit-a2ui-roster.mjs +0 -96
- package/skills/adia-ui-a2ui/scripts/teach-route.mjs +0 -157
- package/skills/adia-ui-a2ui/skill.json +0 -38
- package/skills/adia-ui-authoring/CHANGELOG.md +0 -32
- package/skills/adia-ui-authoring/SKILL.md +0 -256
- package/skills/adia-ui-authoring/assets/case-studies/admin-shell-decomposition.md +0 -101
- package/skills/adia-ui-authoring/assets/case-studies/maxtokens-32768-discovery.md +0 -109
- package/skills/adia-ui-authoring/assets/case-studies/theme-panel-promotion.md +0 -113
- package/skills/adia-ui-authoring/evals/adversarial-design-plan-gates.json +0 -138
- package/skills/adia-ui-authoring/evals/routing-corpus.json +0 -245
- package/skills/adia-ui-authoring/references/canonical-pattern-index.md +0 -179
- package/skills/adia-ui-authoring/references/common-gotchas.md +0 -93
- package/skills/adia-ui-authoring/references/composite-demo-protocol.md +0 -1084
- package/skills/adia-ui-authoring/references/teach-protocol.md +0 -428
- package/skills/adia-ui-authoring/scripts/audit-authoring-roster.mjs +0 -148
- package/skills/adia-ui-authoring/skill.json +0 -45
- package/skills/adia-ui-dogfood/CHANGELOG.md +0 -17
- package/skills/adia-ui-dogfood/README.md +0 -62
- package/skills/adia-ui-dogfood/SKILL.md +0 -866
- package/skills/adia-ui-dogfood/skill.json +0 -40
- package/skills/adia-ui-forge/SKILL.md +0 -88
- package/skills/adia-ui-forge/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-gen-review/CHANGELOG.md +0 -108
- package/skills/adia-ui-gen-review/SKILL.md +0 -266
- package/skills/adia-ui-gen-review/references/loop-protocol.md +0 -712
- package/skills/adia-ui-gen-review/references/rubric-cosmetic.md +0 -144
- package/skills/adia-ui-gen-review/references/rubric-decompose.md +0 -117
- package/skills/adia-ui-gen-review/references/rubric-score.md +0 -249
- package/skills/adia-ui-gen-review/references/teach-protocol.md +0 -214
- package/skills/adia-ui-gen-review/skill.json +0 -23
- package/skills/adia-ui-llm/CHANGELOG.md +0 -25
- package/skills/adia-ui-llm/SKILL.md +0 -165
- package/skills/adia-ui-llm/evals/adversarial-corpus.json +0 -75
- package/skills/adia-ui-llm/evals/routing-corpus.json +0 -30
- package/skills/adia-ui-llm/evals/teach-routing-cases.json +0 -73
- package/skills/adia-ui-llm/references/teach-protocol.md +0 -78
- package/skills/adia-ui-llm/scripts/audit-llm-roster.mjs +0 -93
- package/skills/adia-ui-llm/scripts/teach-route.mjs +0 -119
- package/skills/adia-ui-llm/skill.json +0 -33
- package/skills/adia-ui-ops/CHANGELOG.md +0 -291
- package/skills/adia-ui-ops/SKILL.md +0 -401
- package/skills/adia-ui-ops/references/INDEX.md +0 -158
- package/skills/adia-ui-ops/references/audit-cadence.md +0 -263
- package/skills/adia-ui-ops/references/audit-patterns/archive-link-sweep.md +0 -96
- package/skills/adia-ui-ops/references/audit-patterns/audit-history-ledger.md +0 -162
- package/skills/adia-ui-ops/references/audit-patterns/browser-bundle-node-imports.md +0 -154
- package/skills/adia-ui-ops/references/audit-patterns/changelog-unreleased-bloat.md +0 -75
- package/skills/adia-ui-ops/references/audit-patterns/coverage-gaps.md +0 -187
- package/skills/adia-ui-ops/references/audit-patterns/entry-file-coverage.md +0 -122
- package/skills/adia-ui-ops/references/audit-patterns/format-hygiene.md +0 -217
- package/skills/adia-ui-ops/references/audit-patterns/lockstep-versioning.md +0 -113
- package/skills/adia-ui-ops/references/audit-patterns/memory-fragmentation.md +0 -180
- package/skills/adia-ui-ops/references/audit-patterns/orphan-detection.md +0 -202
- package/skills/adia-ui-ops/references/audit-patterns/pointer-validation.md +0 -180
- package/skills/adia-ui-ops/references/audit-patterns/redundancy-detection.md +0 -210
- package/skills/adia-ui-ops/references/audit-patterns/spec-dating-sweep.md +0 -91
- package/skills/adia-ui-ops/references/audit-patterns/stale-content.md +0 -182
- package/skills/adia-ui-ops/references/audit-patterns/staleness-tooling.md +0 -156
- package/skills/adia-ui-ops/references/audit-patterns/token-waste-detection.md +0 -196
- package/skills/adia-ui-ops/references/doc-types/adr-pattern.md +0 -210
- package/skills/adia-ui-ops/references/doc-types/architecture-md.md +0 -190
- package/skills/adia-ui-ops/references/doc-types/changelog.md +0 -183
- package/skills/adia-ui-ops/references/doc-types/decisions-log.md +0 -146
- package/skills/adia-ui-ops/references/doc-types/plan-roadmap.md +0 -182
- package/skills/adia-ui-ops/references/doc-types/postmortem-pattern.md +0 -206
- package/skills/adia-ui-ops/references/genres/prose-and-writing.md +0 -149
- package/skills/adia-ui-ops/references/guidance/context-budget.md +0 -137
- package/skills/adia-ui-ops/references/guidance/llm-doc-writing.md +0 -116
- package/skills/adia-ui-ops/references/guidance/reliability-dial.md +0 -186
- package/skills/adia-ui-ops/references/recipes/adr-introduction.md +0 -211
- package/skills/adia-ui-ops/references/recipes/audit-existing-repo.md +0 -234
- package/skills/adia-ui-ops/references/recipes/cold-start-harvest.md +0 -263
- package/skills/adia-ui-ops/references/recipes/concurrent-learnings-merge.md +0 -158
- package/skills/adia-ui-ops/references/recipes/continuous-learning-loop.md +0 -169
- package/skills/adia-ui-ops/references/recipes/external-reference-verification.md +0 -158
- package/skills/adia-ui-ops/references/recipes/findings-index-readout.md +0 -126
- package/skills/adia-ui-ops/references/recipes/greenfield-setup.md +0 -252
- package/skills/adia-ui-ops/references/recipes/harvest-repo-brain.md +0 -169
- package/skills/adia-ui-ops/references/recipes/import-repo-brain-harvest.md +0 -153
- package/skills/adia-ui-ops/references/recipes/memory-organization.md +0 -182
- package/skills/adia-ui-ops/references/recipes/recommend-then-validate.md +0 -199
- package/skills/adia-ui-ops/references/recipes/self-healing-hooks.md +0 -366
- package/skills/adia-ui-ops/references/recipes/skill-stewardship-loop.md +0 -113
- package/skills/adia-ui-ops/references/standards/agents-md-spec.md +0 -138
- package/skills/adia-ui-ops/references/standards/claude-md-convention.md +0 -123
- package/skills/adia-ui-ops/references/standards/cross-tool-matrix.md +0 -85
- package/skills/adia-ui-ops/references/standards/readme-conventions.md +0 -232
- package/skills/adia-ui-ops/references/teach-protocol.md +0 -215
- package/skills/adia-ui-ops/scripts/audit-ops-roster.mjs +0 -104
- package/skills/adia-ui-ops/skill.json +0 -65
- package/skills/adia-ui-release/CHANGELOG.md +0 -66
- package/skills/adia-ui-release/SKILL.md +0 -323
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-batch-push-v0.6.14-v0.6.15.md +0 -144
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-corpus-drift-remediation-v0.6.15.md +0 -155
- package/skills/adia-ui-release/assets/case-studies/2026-05-20-version-skip-correction-v0.6.12.md +0 -114
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-author-from-scratch-v0.6.18.md +0 -139
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-feedback37-retraction-v0.6.21.md +0 -124
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-fn1-enrichment-pass-v0.6.19.md +0 -125
- package/skills/adia-ui-release/assets/case-studies/2026-05-21-stale-test-detection-v0.6.20.md +0 -142
- package/skills/adia-ui-release/assets/case-studies/2026-05-23-freshness-gate-recovery-v0.6.32.md +0 -97
- package/skills/adia-ui-release/assets/case-studies/2026-05-26-catalog-drift-recurring-v0.6.40.md +0 -147
- package/skills/adia-ui-release/assets/templates/stub-changelog.template.md +0 -22
- package/skills/adia-ui-release/evals/evals.json +0 -164
- package/skills/adia-ui-release/references/changelog-discipline.md +0 -250
- package/skills/adia-ui-release/references/cycle-happy-path.md +0 -520
- package/skills/adia-ui-release/references/exe-deploy.md +0 -149
- package/skills/adia-ui-release/references/gates-catalog.md +0 -778
- package/skills/adia-ui-release/references/independent-package-release.md +0 -129
- package/skills/adia-ui-release/references/ledger-discipline.md +0 -232
- package/skills/adia-ui-release/references/migration-guide-authoring.md +0 -174
- package/skills/adia-ui-release/references/multi-agent-baseline.md +0 -207
- package/skills/adia-ui-release/references/notes-authoring.md +0 -212
- package/skills/adia-ui-release/references/recovery-paths.md +0 -262
- package/skills/adia-ui-release/references/rollup-notes.md +0 -208
- package/skills/adia-ui-release/references/teach-protocol.md +0 -468
- package/skills/adia-ui-release/scripts/audit-gate-roster.mjs +0 -196
- package/skills/adia-ui-release/scripts/make-ledger.mjs +0 -200
- package/skills/adia-ui-release/skill.json +0 -77
- package/skills/dogfood-sweep/CHANGELOG.md +0 -37
- package/skills/dogfood-sweep/README.md +0 -105
- package/skills/dogfood-sweep/SKILL.md +0 -1000
- package/skills/dogfood-sweep/analyze.mjs +0 -600
- package/skills/dogfood-sweep/skill.json +0 -31
- /package/skills/{adia-ui-authoring → adia-author}/references/api-contract.md +0 -0
- /package/skills/{adia-ui-release → adia-release}/scripts/assert-monorepo-root.mjs +0 -0
|
@@ -1,600 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Dogfooding analyzer — walks every `/site/components/*` demo page in a
|
|
4
|
-
* headless browser and runs visual-correctness probes that type-checks
|
|
5
|
-
* and tests don't catch.
|
|
6
|
-
*
|
|
7
|
-
* Probes:
|
|
8
|
-
* 1. Zero-area — `*-ui` elements that render at 0×0 (e.g. parent
|
|
9
|
-
* `display:none`, children moved into an overflow
|
|
10
|
-
* popover, contents collapsed).
|
|
11
|
-
* 2. Transparent — swatches/badges/indicators with known
|
|
12
|
-
* "should-be-colored" semantics whose computed
|
|
13
|
-
* background is `rgba(0,0,0,0)` (chart-legend bug
|
|
14
|
-
* class — a fallback token that doesn't resolve).
|
|
15
|
-
* 3. Empty-control — form controls (`input-ui`, `search-ui`, etc.)
|
|
16
|
-
* whose `connected()` should have stamped an
|
|
17
|
-
* `<input>` or `[contenteditable]` but didn't.
|
|
18
|
-
* 4. Synonym-attr — known synonym-attribute / synonym-slot drift
|
|
19
|
-
* classes documented in
|
|
20
|
-
* `docs/conventions/attribute-api-migration.md`.
|
|
21
|
-
* 5. Console — errors + deprecation warnings during page
|
|
22
|
-
* load + 800ms settling.
|
|
23
|
-
*
|
|
24
|
-
* Output:
|
|
25
|
-
* docs/reports/dogfooding-YYYY-MM-DD.md (or path from --out)
|
|
26
|
-
* Severity levels: critical (page broken), warning (likely-broken),
|
|
27
|
-
* info (drift, deprecation, non-blocking).
|
|
28
|
-
*
|
|
29
|
-
* Usage:
|
|
30
|
-
* node .agents/skills/dogfood-sweep/analyze.mjs # full sweep
|
|
31
|
-
* node .agents/skills/dogfood-sweep/analyze.mjs --filter chart-legend # one page
|
|
32
|
-
* node .agents/skills/dogfood-sweep/analyze.mjs --port 5173 # custom port
|
|
33
|
-
* node .agents/skills/dogfood-sweep/analyze.mjs --out /tmp/report.md # custom path
|
|
34
|
-
* node .agents/skills/dogfood-sweep/analyze.mjs --quiet # report only
|
|
35
|
-
*
|
|
36
|
-
* Assumes the dev server is already running. Spin one up first:
|
|
37
|
-
* npm run dev # in a separate terminal
|
|
38
|
-
*/
|
|
39
|
-
|
|
40
|
-
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
41
|
-
import { join, dirname } from 'node:path';
|
|
42
|
-
import { fileURLToPath } from 'node:url';
|
|
43
|
-
import { chromium } from 'playwright';
|
|
44
|
-
|
|
45
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
46
|
-
// SoT lives at packages/plugins/adia-ui-forge/skills/dogfood-sweep/ → repo root
|
|
47
|
-
// is five levels up (ADR-0040 Phase 4 carve; the .agents/skills/dogfood-sweep
|
|
48
|
-
// path is a symlink, and Node resolves import.meta.url to the real file location).
|
|
49
|
-
const REPO_ROOT = join(__dirname, '..', '..', '..', '..', '..');
|
|
50
|
-
|
|
51
|
-
// ── Args ──────────────────────────────────────────────────────────────────
|
|
52
|
-
|
|
53
|
-
const args = process.argv.slice(2);
|
|
54
|
-
const argVal = (flag) => {
|
|
55
|
-
const i = args.indexOf(flag);
|
|
56
|
-
return i >= 0 && args[i + 1] && !args[i + 1].startsWith('--') ? args[i + 1] : null;
|
|
57
|
-
};
|
|
58
|
-
const FILTER = argVal('--filter');
|
|
59
|
-
const PORT = parseInt(argVal('--port') || '5173', 10);
|
|
60
|
-
const QUIET = args.includes('--quiet');
|
|
61
|
-
const today = new Date().toISOString().slice(0, 10);
|
|
62
|
-
const OUT = argVal('--out') || join(REPO_ROOT, 'docs/reports', `dogfooding-${today}.md`);
|
|
63
|
-
|
|
64
|
-
const log = (...m) => { if (!QUIET) console.error(...m); };
|
|
65
|
-
|
|
66
|
-
// ── Component contracts (what each component MUST stamp at render) ────────
|
|
67
|
-
//
|
|
68
|
-
// Narrow on purpose: most AdiaUI components are *the* interactive surface
|
|
69
|
-
// (role="button" + tabindex + own listeners), not host-of-native-control.
|
|
70
|
-
// Listing them here as "must have a <button> child" produces 300+ false
|
|
71
|
-
// positives. The genuinely-stamping components are input-ui (creates an
|
|
72
|
-
// `<input>` or `<span contenteditable>`) and search-ui (wraps input-ui).
|
|
73
|
-
|
|
74
|
-
const STAMP_CONTRACTS = {
|
|
75
|
-
'input-ui': {
|
|
76
|
-
reason: 'connected() should stamp <input slot="text"> or <span slot="text" contenteditable>',
|
|
77
|
-
},
|
|
78
|
-
'search-ui': {
|
|
79
|
-
reason: 'connected() should stamp <input-ui type="search"> internally',
|
|
80
|
-
},
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
// ── Selectors that should resolve to a non-transparent background ─────────
|
|
84
|
-
//
|
|
85
|
-
// `[data-swatch]`, semantic-variant pills/buttons, and chart indicators.
|
|
86
|
-
// rgba(0,0,0,0) on these = bug (the chart-legend class).
|
|
87
|
-
|
|
88
|
-
const COLORED_SELECTORS = [
|
|
89
|
-
// chart-legend / chart-tooltip swatches
|
|
90
|
-
'[data-swatch]',
|
|
91
|
-
'[data-indicator]',
|
|
92
|
-
// semantic variants on tag-ui / button-ui / alert-ui
|
|
93
|
-
'tag-ui[variant=primary]',
|
|
94
|
-
'tag-ui[variant=success]',
|
|
95
|
-
'tag-ui[variant=warning]',
|
|
96
|
-
'tag-ui[variant=danger]',
|
|
97
|
-
'tag-ui[variant=info]',
|
|
98
|
-
'button-ui[variant=primary]:not([disabled])',
|
|
99
|
-
'button-ui[color=danger]:not([disabled])',
|
|
100
|
-
];
|
|
101
|
-
|
|
102
|
-
// ── Synonym-attribute / synonym-slot drift markers ────────────────────────
|
|
103
|
-
//
|
|
104
|
-
// Mirrors `docs/conventions/attribute-api-migration.md`. These run via
|
|
105
|
-
// querySelectorAll; matching elements are flagged as info-level.
|
|
106
|
-
|
|
107
|
-
const DRIFT_MARKERS = [
|
|
108
|
-
{ selector: 'avatar-ui[name]', message: '<avatar-ui name=…> is deprecated; use text=…' },
|
|
109
|
-
{ selector: 'grid-ui[cols]', message: '<grid-ui cols=…> is the synonym; canonical is columns=…' },
|
|
110
|
-
{ selector: 'stepper-ui[current]', message: '<stepper-ui current=…> is the synonym; canonical is step=…' },
|
|
111
|
-
{ selector: 'stepper-item-ui[state]', message: '<stepper-item-ui state=…> is the synonym; parent <stepper-ui step> drives it' },
|
|
112
|
-
{ selector: 'card-ui [slot="meta"]', message: 'card-ui has no slot="meta" rule — tag falls into bare grid cell and stretches' },
|
|
113
|
-
];
|
|
114
|
-
|
|
115
|
-
// ── Probes (all run in-page via page.evaluate) ────────────────────────────
|
|
116
|
-
|
|
117
|
-
async function runProbes(page) {
|
|
118
|
-
return await page.evaluate(({ COLORED_SELECTORS, DRIFT_MARKERS }) => {
|
|
119
|
-
const findings = [];
|
|
120
|
-
const push = (severity, kind, detail) => findings.push({ severity, kind, ...detail });
|
|
121
|
-
|
|
122
|
-
// 1. Zero-area
|
|
123
|
-
// Walk artifact items; skip elements that are intentionally hidden
|
|
124
|
-
// (any display:none / visibility:hidden / [hidden] ancestor — e.g.
|
|
125
|
-
// avatar-group's overflow children, popover panels, drawer contents)
|
|
126
|
-
// or that are "rendered-on-demand" components which only paint when
|
|
127
|
-
// opened (drawers, modals, popovers, tooltips, toasts).
|
|
128
|
-
const ON_DEMAND_TAGS = new Set([
|
|
129
|
-
'drawer-ui', 'modal-ui', 'popover-ui', 'tooltip-ui', 'toast-ui',
|
|
130
|
-
'context-menu-ui', 'sheet-ui', 'dialog-ui',
|
|
131
|
-
]);
|
|
132
|
-
const isIntentionallyHidden = (el) => {
|
|
133
|
-
let p = el;
|
|
134
|
-
while (p && p.nodeType === 1) {
|
|
135
|
-
if (p.hasAttribute?.('hidden')) return true;
|
|
136
|
-
if (p.hasAttribute?.('popover')) return true; // popover API closed
|
|
137
|
-
if (p.tagName === 'DIALOG' && !p.open) return true;
|
|
138
|
-
if (ON_DEMAND_TAGS.has(p.tagName?.toLowerCase())) return true;
|
|
139
|
-
const cs = getComputedStyle(p);
|
|
140
|
-
if (cs.display === 'none' || cs.visibility === 'hidden') return true;
|
|
141
|
-
// Stop walking at the artifact-item boundary.
|
|
142
|
-
if (p.matches?.('[data-artifact-item]')) return false;
|
|
143
|
-
p = p.parentElement;
|
|
144
|
-
}
|
|
145
|
-
return false;
|
|
146
|
-
};
|
|
147
|
-
document.querySelectorAll('[data-artifact-item] *').forEach((el) => {
|
|
148
|
-
if (!el.tagName.endsWith('-UI')) return;
|
|
149
|
-
if (ON_DEMAND_TAGS.has(el.tagName.toLowerCase())) return;
|
|
150
|
-
if (isIntentionallyHidden(el)) return;
|
|
151
|
-
const r = el.getBoundingClientRect();
|
|
152
|
-
// Only flag elements that should have rendered something — skip those
|
|
153
|
-
// with no children, no text, and no [icon]/[text] attrs.
|
|
154
|
-
const hasIntent =
|
|
155
|
-
el.children.length > 0 ||
|
|
156
|
-
(el.textContent || '').trim().length > 0 ||
|
|
157
|
-
el.hasAttribute('icon') ||
|
|
158
|
-
el.hasAttribute('text') ||
|
|
159
|
-
el.hasAttribute('label');
|
|
160
|
-
if (hasIntent && (r.width === 0 || r.height === 0)) {
|
|
161
|
-
push('critical', 'zero-area', {
|
|
162
|
-
tag: el.tagName.toLowerCase(),
|
|
163
|
-
label: el.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
164
|
-
rect: { w: Math.round(r.width), h: Math.round(r.height) },
|
|
165
|
-
hint: el.parentElement?.tagName === 'TOOLBAR-UI'
|
|
166
|
-
? 'parent toolbar-ui — likely overflow="menu" spilled it into the popover'
|
|
167
|
-
: null,
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
// 2. Transparent-where-colored
|
|
173
|
-
for (const selector of COLORED_SELECTORS) {
|
|
174
|
-
document.querySelectorAll(selector).forEach((el) => {
|
|
175
|
-
const r = el.getBoundingClientRect();
|
|
176
|
-
if (r.width === 0 || r.height === 0) return; // already flagged or hidden
|
|
177
|
-
const cs = getComputedStyle(el);
|
|
178
|
-
const bg = cs.backgroundColor;
|
|
179
|
-
const borderTop = cs.borderTopColor;
|
|
180
|
-
const isTransparent = bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent';
|
|
181
|
-
const borderTransparent = !borderTop || borderTop === 'rgba(0, 0, 0, 0)';
|
|
182
|
-
// [data-swatch] for shape="dashed" intentionally has transparent bg
|
|
183
|
-
// and paints via border-top instead. Test that directly: any
|
|
184
|
-
// colored top border with non-zero width is "by design".
|
|
185
|
-
const paintsViaBorder =
|
|
186
|
-
el.matches('[data-swatch]') &&
|
|
187
|
-
parseFloat(cs.borderTopWidth) > 0 &&
|
|
188
|
-
!borderTransparent;
|
|
189
|
-
if (paintsViaBorder) return;
|
|
190
|
-
if (isTransparent) {
|
|
191
|
-
push('critical', 'transparent-fill', {
|
|
192
|
-
selector,
|
|
193
|
-
tag: el.tagName.toLowerCase(),
|
|
194
|
-
label: el.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
195
|
-
inlineSwatchVar: el.style.getPropertyValue('--swatch-color') || null,
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// 3. Synonym-attr / synonym-slot drift
|
|
202
|
-
for (const { selector, message } of DRIFT_MARKERS) {
|
|
203
|
-
document.querySelectorAll(selector).forEach((el) => {
|
|
204
|
-
push('info', 'drift', {
|
|
205
|
-
selector,
|
|
206
|
-
message,
|
|
207
|
-
tag: el.tagName.toLowerCase(),
|
|
208
|
-
label: el.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
209
|
-
});
|
|
210
|
-
});
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// 4. Multi-child rich content inside alert-ui (no col-ui wrap)
|
|
214
|
-
document.querySelectorAll('alert-ui').forEach((alert) => {
|
|
215
|
-
const direct = Array.from(alert.children).filter(
|
|
216
|
-
(c) => !c.hasAttribute('slot'),
|
|
217
|
-
);
|
|
218
|
-
if (direct.length >= 2 && direct.every((c) => c.tagName === 'TEXT-UI')) {
|
|
219
|
-
push('warning', 'alert-flex-row', {
|
|
220
|
-
tag: 'alert-ui',
|
|
221
|
-
label: alert.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
222
|
-
message: 'multiple bare <text-ui> inside alert-ui lay out as flex-row — wrap in <col-ui slot="content">',
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
// 5. Missing component CSS — for each unique *-ui tag rendered on
|
|
228
|
-
// the page, verify a stylesheet matching `/components/{prefix}/{prefix}.css`
|
|
229
|
-
// is loaded. Catches the "swap to <select-ui> but forget to <link>
|
|
230
|
-
// select.css" class — markup looks right, JS registers the element,
|
|
231
|
-
// every other probe passes, but the listbox renders unstyled.
|
|
232
|
-
//
|
|
233
|
-
// Demo pages under /site/components/<X>/ usually embed all sibling
|
|
234
|
-
// components transitively (their docs router shell loads tokens.css
|
|
235
|
-
// + a barrel, so most components resolve). When this probe fires
|
|
236
|
-
// on a demo page, it's signaling a real authoring miss — either
|
|
237
|
-
// a link tag was dropped or a new component on the page wasn't
|
|
238
|
-
// added to the shell loader.
|
|
239
|
-
// Components / infra elements that legitimately ship without a sibling
|
|
240
|
-
// {prefix}/{prefix}.css under packages/web-components/components/. Two
|
|
241
|
-
// groups:
|
|
242
|
-
//
|
|
243
|
-
// 1. CSS-only components (yaml + a2ui.json only, styled by parent slot
|
|
244
|
-
// rules — per `feedback_css_only_components`). Source of truth:
|
|
245
|
-
// `for d in components/*/; do n="${d%/}"; [ ! -f "$d$n.css" ] &&
|
|
246
|
-
// echo "$n"; done` enumerates them.
|
|
247
|
-
//
|
|
248
|
-
// 2. Infrastructure elements registered outside components/ (e.g.
|
|
249
|
-
// `router-ui` from core/provider.js). They have inline / shell
|
|
250
|
-
// stylesheets that don't match the path pattern.
|
|
251
|
-
//
|
|
252
|
-
// When this list grows: re-run the source-of-truth grep above + audit
|
|
253
|
-
// `customElements.define(...)` calls under packages/web-components/core/.
|
|
254
|
-
const COMPONENTS_WITHOUT_OWN_CSS = new Set([
|
|
255
|
-
// CSS-only components (no .css sibling) — styled by parent slot rules
|
|
256
|
-
'aside-ui', 'footer-ui', 'header-ui', 'section-ui', 'stat-ui',
|
|
257
|
-
// Co-located siblings (CSS lives in the parent component's directory)
|
|
258
|
-
'tab-ui', // css + js in components/tabs/, not components/tab/
|
|
259
|
-
// Module-tier composites with no own CSS (styled by shell/parent)
|
|
260
|
-
'chat-thread-ui',
|
|
261
|
-
// Infrastructure elements (registered outside components/)
|
|
262
|
-
'router-ui',
|
|
263
|
-
]);
|
|
264
|
-
const allCustomTags = new Set();
|
|
265
|
-
document.querySelectorAll('*').forEach((el) => {
|
|
266
|
-
if (el.tagName.endsWith('-UI') && !ON_DEMAND_TAGS.has(el.tagName.toLowerCase())) {
|
|
267
|
-
allCustomTags.add(el.tagName.toLowerCase());
|
|
268
|
-
}
|
|
269
|
-
});
|
|
270
|
-
const loadedHrefs = Array.from(document.styleSheets)
|
|
271
|
-
.map((s) => { try { return s.href || ''; } catch { return ''; } })
|
|
272
|
-
.filter(Boolean);
|
|
273
|
-
for (const tag of allCustomTags) {
|
|
274
|
-
if (COMPONENTS_WITHOUT_OWN_CSS.has(tag)) continue;
|
|
275
|
-
const prefix = tag.replace(/-ui$/, '');
|
|
276
|
-
const expected = `/components/${prefix}/${prefix}.css`;
|
|
277
|
-
const linked = loadedHrefs.some((h) => h.includes(expected));
|
|
278
|
-
if (linked) continue;
|
|
279
|
-
// Heuristic fallback: if at least one conventional component-prefixed
|
|
280
|
-
// CSS custom property resolves on a rendered instance, the stylesheet
|
|
281
|
-
// probably loaded under a different URL pattern (bundled / aliased).
|
|
282
|
-
const inst = document.querySelector(tag);
|
|
283
|
-
let resolved = false;
|
|
284
|
-
if (inst) {
|
|
285
|
-
const cs = getComputedStyle(inst);
|
|
286
|
-
// Primary probe: standard token naming convention
|
|
287
|
-
const probeNames = [
|
|
288
|
-
`--${prefix}-bg`, `--${prefix}-fg`, `--${prefix}-pad`,
|
|
289
|
-
`--${prefix}-radius`, `--${prefix}-size`, `--${prefix}-gap`,
|
|
290
|
-
`--${prefix}-border`,
|
|
291
|
-
];
|
|
292
|
-
resolved = probeNames.some((n) => cs.getPropertyValue(n).trim().length > 0);
|
|
293
|
-
// Broader fallback: scan all custom properties for any --{prefix}-* token.
|
|
294
|
-
// Catches components that use sub-namespaced tokens (e.g. --menu-popover-bg,
|
|
295
|
-
// --toggle-option-gap) rather than the flat --{prefix}-bg convention.
|
|
296
|
-
if (!resolved) {
|
|
297
|
-
const propPrefix = `--${prefix}-`;
|
|
298
|
-
for (const prop of cs) {
|
|
299
|
-
if (prop.startsWith(propPrefix) && cs.getPropertyValue(prop).trim().length > 0) {
|
|
300
|
-
resolved = true;
|
|
301
|
-
break;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
if (!resolved) {
|
|
307
|
-
push('critical', 'missing-component-css', {
|
|
308
|
-
tag,
|
|
309
|
-
label: inst?.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
310
|
-
message: `<${tag}> rendered but no stylesheet matching ${expected} loaded — controls render unstyled (popover bg transparent, listbox = raw text). Add the <link rel="stylesheet"> in the page or shell.`,
|
|
311
|
-
expectedHref: expected,
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// 6. Unstyled popover — runtime check for [popover]:popover-open
|
|
317
|
-
// whose computed background is transparent + zero padding. This
|
|
318
|
-
// is the visual symptom of missing-component-css for a popover-
|
|
319
|
-
// bearing primitive (select-ui listbox, tooltip-ui, etc.). Catches
|
|
320
|
-
// the case from a different angle in case the URL heuristic above
|
|
321
|
-
// didn't match a particular bundling scheme.
|
|
322
|
-
// Popovers whose host element is intentionally transparent — visual
|
|
323
|
-
// background comes from child elements, not the host itself.
|
|
324
|
-
const TRANSPARENT_HOST_POPOVERS = new Set([
|
|
325
|
-
'feed-ui', // toast feed: host is transparent; feed-item-ui children carry bg
|
|
326
|
-
]);
|
|
327
|
-
document.querySelectorAll('[popover]').forEach((el) => {
|
|
328
|
-
if (!el.matches?.(':popover-open')) return;
|
|
329
|
-
if (TRANSPARENT_HOST_POPOVERS.has(el.tagName.toLowerCase())) return;
|
|
330
|
-
const cs = getComputedStyle(el);
|
|
331
|
-
const transparent = cs.backgroundColor === 'rgba(0, 0, 0, 0)' || cs.backgroundColor === 'transparent';
|
|
332
|
-
const noPad = parseFloat(cs.paddingTop) === 0 && parseFloat(cs.paddingLeft) === 0;
|
|
333
|
-
if (transparent && noPad) {
|
|
334
|
-
push('critical', 'unstyled-popover', {
|
|
335
|
-
tag: el.tagName.toLowerCase(),
|
|
336
|
-
host: el.parentElement?.tagName?.toLowerCase() || null,
|
|
337
|
-
label: el.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
338
|
-
message: 'open popover has transparent bg + zero padding — host component CSS likely missing',
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
|
|
343
|
-
return findings;
|
|
344
|
-
}, { COLORED_SELECTORS, DRIFT_MARKERS });
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
async function runStampContractProbe(page) {
|
|
348
|
-
// Functions don't survive page.evaluate serialization — pass tag+reason
|
|
349
|
-
// pairs only and re-implement the predicate in-page below.
|
|
350
|
-
const reasons = Object.fromEntries(
|
|
351
|
-
Object.entries(STAMP_CONTRACTS).map(([k, v]) => [k, v.reason]),
|
|
352
|
-
);
|
|
353
|
-
return await page.evaluate((contracts) => {
|
|
354
|
-
const findings = [];
|
|
355
|
-
for (const [tag, reason] of Object.entries(contracts)) {
|
|
356
|
-
document.querySelectorAll(`${tag}`).forEach((el) => {
|
|
357
|
-
// Skip elements clearly outside artifact frames (icons in nav, etc.).
|
|
358
|
-
if (!el.closest('[data-artifact-item]')) return;
|
|
359
|
-
const r = el.getBoundingClientRect();
|
|
360
|
-
if (r.width === 0 || r.height === 0) return;
|
|
361
|
-
// Re-implement contracts in-page (functions don't survive serialization).
|
|
362
|
-
let ok = true;
|
|
363
|
-
if (tag === 'input-ui') ok = !!el.querySelector('[slot="text"]');
|
|
364
|
-
else if (tag === 'search-ui') ok = !!el.querySelector('input-ui');
|
|
365
|
-
if (!ok) {
|
|
366
|
-
findings.push({
|
|
367
|
-
severity: 'critical',
|
|
368
|
-
kind: 'empty-control',
|
|
369
|
-
tag,
|
|
370
|
-
label: el.closest('[data-artifact-label]')?.dataset.artifactLabel || null,
|
|
371
|
-
reason,
|
|
372
|
-
});
|
|
373
|
-
}
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
return findings;
|
|
377
|
-
}, reasons);
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// ── Sweep ─────────────────────────────────────────────────────────────────
|
|
381
|
-
|
|
382
|
-
async function loadComponentRoutes() {
|
|
383
|
-
const sitemap = JSON.parse(await readFile(join(REPO_ROOT, 'site/sitemap.json'), 'utf8'));
|
|
384
|
-
const routes = [];
|
|
385
|
-
// Sitemap shape varies by section; recurse over every key generically
|
|
386
|
-
// and pick up nodes that have a `/site/components/*` path + content.
|
|
387
|
-
const walk = (node, depth = 0) => {
|
|
388
|
-
if (!node || depth > 12) return;
|
|
389
|
-
if (Array.isArray(node)) return node.forEach((n) => walk(n, depth));
|
|
390
|
-
if (typeof node !== 'object') return;
|
|
391
|
-
if (node.path?.startsWith('/site/components/') && node.content) {
|
|
392
|
-
routes.push({ path: node.path, content: node.content, name: node.path.split('/').pop() });
|
|
393
|
-
}
|
|
394
|
-
for (const v of Object.values(node)) walk(v, depth + 1);
|
|
395
|
-
};
|
|
396
|
-
walk(sitemap);
|
|
397
|
-
return FILTER ? routes.filter((r) => r.name.includes(FILTER)) : routes;
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
async function probePage(browser, base, route) {
|
|
401
|
-
const ctx = await browser.newContext({ viewport: { width: 1280, height: 900 } });
|
|
402
|
-
const page = await ctx.newPage();
|
|
403
|
-
const consoleEntries = [];
|
|
404
|
-
page.on('console', (msg) => {
|
|
405
|
-
const type = msg.type();
|
|
406
|
-
if (type === 'error' || type === 'warning') {
|
|
407
|
-
consoleEntries.push({ type, text: msg.text().slice(0, 240) });
|
|
408
|
-
}
|
|
409
|
-
});
|
|
410
|
-
page.on('pageerror', (err) => consoleEntries.push({ type: 'error', text: err.message.slice(0, 240) }));
|
|
411
|
-
|
|
412
|
-
// Hit the route path (not the raw `content` HTML) — the docs shell is
|
|
413
|
-
// what loads the component bundle. Visiting the raw HTML directly leaves
|
|
414
|
-
// every `*-ui` element inert and makes every probe report 0×0.
|
|
415
|
-
const url = `${base}${route.path}`;
|
|
416
|
-
try {
|
|
417
|
-
await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 });
|
|
418
|
-
} catch (err) {
|
|
419
|
-
await ctx.close();
|
|
420
|
-
return {
|
|
421
|
-
route: route.path,
|
|
422
|
-
error: `navigation failed: ${err.message}`,
|
|
423
|
-
findings: [],
|
|
424
|
-
consoleEntries: [],
|
|
425
|
-
};
|
|
426
|
-
}
|
|
427
|
-
// Settle: components often stamp on connected() + microtasks.
|
|
428
|
-
await page.waitForTimeout(800);
|
|
429
|
-
|
|
430
|
-
const probeFindings = await runProbes(page);
|
|
431
|
-
const stampFindings = await runStampContractProbe(page);
|
|
432
|
-
const findings = [...probeFindings, ...stampFindings];
|
|
433
|
-
|
|
434
|
-
await ctx.close();
|
|
435
|
-
return { route: route.path, findings, consoleEntries };
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
// ── Report ────────────────────────────────────────────────────────────────
|
|
439
|
-
|
|
440
|
-
function severityOrder(s) {
|
|
441
|
-
return { critical: 0, warning: 1, info: 2 }[s] ?? 3;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
function renderReport(results, base) {
|
|
445
|
-
const all = results.flatMap((r) =>
|
|
446
|
-
r.findings.map((f) => ({ ...f, route: r.route })),
|
|
447
|
-
);
|
|
448
|
-
const errors = results.filter((r) => r.error);
|
|
449
|
-
const consoleErrors = results.flatMap((r) =>
|
|
450
|
-
r.consoleEntries
|
|
451
|
-
.filter((e) => e.type === 'error')
|
|
452
|
-
.map((e) => ({ ...e, route: r.route })),
|
|
453
|
-
);
|
|
454
|
-
const consoleWarnings = results.flatMap((r) =>
|
|
455
|
-
r.consoleEntries
|
|
456
|
-
.filter((e) => e.type === 'warning')
|
|
457
|
-
.map((e) => ({ ...e, route: r.route })),
|
|
458
|
-
);
|
|
459
|
-
|
|
460
|
-
const bySeverity = (sev) =>
|
|
461
|
-
all.filter((f) => f.severity === sev).sort((a, b) => a.route.localeCompare(b.route));
|
|
462
|
-
const critical = bySeverity('critical');
|
|
463
|
-
const warning = bySeverity('warning');
|
|
464
|
-
const info = bySeverity('info');
|
|
465
|
-
|
|
466
|
-
const renderFinding = (f) => {
|
|
467
|
-
const head = `- **${f.kind}** on \`${f.route}\``;
|
|
468
|
-
const labelBit = f.label ? ` (${f.label})` : '';
|
|
469
|
-
const detailBits = [];
|
|
470
|
-
if (f.tag) detailBits.push(`tag=\`${f.tag}\``);
|
|
471
|
-
if (f.selector) detailBits.push(`selector=\`${f.selector}\``);
|
|
472
|
-
if (f.rect) detailBits.push(`rect=${f.rect.w}×${f.rect.h}`);
|
|
473
|
-
if (f.inlineSwatchVar) detailBits.push(`swatch=\`${f.inlineSwatchVar}\``);
|
|
474
|
-
if (f.hint) detailBits.push(f.hint);
|
|
475
|
-
if (f.reason) detailBits.push(f.reason);
|
|
476
|
-
if (f.message) detailBits.push(f.message);
|
|
477
|
-
return `${head}${labelBit} — ${detailBits.join(' · ')}`;
|
|
478
|
-
};
|
|
479
|
-
|
|
480
|
-
const lines = [];
|
|
481
|
-
lines.push(`# Dogfooding report — ${today}`);
|
|
482
|
-
lines.push('');
|
|
483
|
-
lines.push(`Sweep of every \`/site/components/*\` demo page against ${base}.`);
|
|
484
|
-
lines.push('Generated by `node .agents/skills/dogfood-sweep/analyze.mjs`.');
|
|
485
|
-
lines.push('');
|
|
486
|
-
lines.push('## Summary');
|
|
487
|
-
lines.push('');
|
|
488
|
-
lines.push(`| Severity | Count |`);
|
|
489
|
-
lines.push(`|----------|------:|`);
|
|
490
|
-
lines.push(`| critical | ${critical.length} |`);
|
|
491
|
-
lines.push(`| warning | ${warning.length} |`);
|
|
492
|
-
lines.push(`| info | ${info.length} |`);
|
|
493
|
-
lines.push(`| console errors | ${consoleErrors.length} |`);
|
|
494
|
-
lines.push(`| console warnings | ${consoleWarnings.length} |`);
|
|
495
|
-
lines.push(`| pages scanned | ${results.length} |`);
|
|
496
|
-
lines.push(`| navigation errors | ${errors.length} |`);
|
|
497
|
-
lines.push('');
|
|
498
|
-
|
|
499
|
-
if (critical.length) {
|
|
500
|
-
lines.push(`## Critical (${critical.length})`);
|
|
501
|
-
lines.push('');
|
|
502
|
-
lines.push('Page is visibly broken — element collapsed, swatch transparent, control un-stamped.');
|
|
503
|
-
lines.push('');
|
|
504
|
-
critical.forEach((f) => lines.push(renderFinding(f)));
|
|
505
|
-
lines.push('');
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
if (warning.length) {
|
|
509
|
-
lines.push(`## Warning (${warning.length})`);
|
|
510
|
-
lines.push('');
|
|
511
|
-
lines.push('Likely-broken composition the layout will silently mis-render.');
|
|
512
|
-
lines.push('');
|
|
513
|
-
warning.forEach((f) => lines.push(renderFinding(f)));
|
|
514
|
-
lines.push('');
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
if (info.length) {
|
|
518
|
-
lines.push(`## Info (${info.length})`);
|
|
519
|
-
lines.push('');
|
|
520
|
-
lines.push('Synonym-attribute / deprecation drift. Not breaking; sweep when convenient.');
|
|
521
|
-
lines.push('');
|
|
522
|
-
info.forEach((f) => lines.push(renderFinding(f)));
|
|
523
|
-
lines.push('');
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
if (consoleErrors.length) {
|
|
527
|
-
lines.push(`## Console errors (${consoleErrors.length})`);
|
|
528
|
-
lines.push('');
|
|
529
|
-
consoleErrors.slice(0, 50).forEach((e) =>
|
|
530
|
-
lines.push(`- \`${e.route}\` — ${e.text}`),
|
|
531
|
-
);
|
|
532
|
-
if (consoleErrors.length > 50) {
|
|
533
|
-
lines.push(`- … ${consoleErrors.length - 50} more truncated`);
|
|
534
|
-
}
|
|
535
|
-
lines.push('');
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
if (errors.length) {
|
|
539
|
-
lines.push(`## Navigation errors (${errors.length})`);
|
|
540
|
-
lines.push('');
|
|
541
|
-
errors.forEach((r) => lines.push(`- \`${r.route}\` — ${r.error}`));
|
|
542
|
-
lines.push('');
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
lines.push('---');
|
|
546
|
-
lines.push('');
|
|
547
|
-
lines.push('## Probe coverage');
|
|
548
|
-
lines.push('');
|
|
549
|
-
lines.push('1. **Zero-area** — `*-ui` element with intent (children, text, icon attr) but `getBoundingClientRect()` width or height = 0.');
|
|
550
|
-
lines.push('2. **Transparent fill** — `[data-swatch]`, semantic-variant pills/buttons, chart indicators with `background-color: rgba(0,0,0,0)`. Bug class: chart-legend `--chart-N` fallback that doesn\'t resolve standalone.');
|
|
551
|
-
lines.push('3. **Empty control** — `input-ui`, `search-ui`, `button-ui`, `select-ui`, `tag-ui` whose `connected()` should have stamped internals but didn\'t.');
|
|
552
|
-
lines.push('4. **Synonym-attr / synonym-slot drift** — markers documented in `docs/conventions/attribute-api-migration.md`.');
|
|
553
|
-
lines.push('5. **Alert flex-row** — `alert-ui` with multiple bare `<text-ui>` children (need `<col-ui slot="content">` wrap).');
|
|
554
|
-
lines.push('6. **Console** — every `console.error` and `console.warn` during page load + 800ms settling.');
|
|
555
|
-
lines.push('');
|
|
556
|
-
|
|
557
|
-
return lines.join('\n');
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
// ── Main ──────────────────────────────────────────────────────────────────
|
|
561
|
-
|
|
562
|
-
async function main() {
|
|
563
|
-
const base = `http://localhost:${PORT}`;
|
|
564
|
-
const routes = await loadComponentRoutes();
|
|
565
|
-
log(`▶ Dogfooding sweep — ${routes.length} component pages against ${base}`);
|
|
566
|
-
|
|
567
|
-
const browser = await chromium.launch({ headless: true });
|
|
568
|
-
const results = [];
|
|
569
|
-
let i = 0;
|
|
570
|
-
for (const route of routes) {
|
|
571
|
-
i += 1;
|
|
572
|
-
log(` [${i}/${routes.length}] ${route.path}`);
|
|
573
|
-
try {
|
|
574
|
-
const r = await probePage(browser, base, route);
|
|
575
|
-
results.push(r);
|
|
576
|
-
} catch (err) {
|
|
577
|
-
results.push({ route: route.path, error: err.message, findings: [], consoleEntries: [] });
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
await browser.close();
|
|
581
|
-
|
|
582
|
-
const report = renderReport(results, base);
|
|
583
|
-
await mkdir(dirname(OUT), { recursive: true });
|
|
584
|
-
await writeFile(OUT, report, 'utf8');
|
|
585
|
-
|
|
586
|
-
const total = results.reduce((acc, r) => acc + r.findings.length, 0);
|
|
587
|
-
const critical = results.reduce(
|
|
588
|
-
(acc, r) => acc + r.findings.filter((f) => f.severity === 'critical').length,
|
|
589
|
-
0,
|
|
590
|
-
);
|
|
591
|
-
log(`✔ ${total} findings (${critical} critical) → ${OUT}`);
|
|
592
|
-
|
|
593
|
-
// Exit code: 1 if any critical, 0 otherwise. CI-friendly.
|
|
594
|
-
process.exit(critical > 0 ? 1 : 0);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
main().catch((err) => {
|
|
598
|
-
console.error('dogfood analyzer crashed:', err);
|
|
599
|
-
process.exit(2);
|
|
600
|
-
});
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "dogfood-sweep",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Six-mode static + visual sweep across /site/components/*, apps/, playgrounds/, catalog/. Use whenever the user wants to FIND VISUAL BUGS / AUDIT NATIVE PRIMITIVE LEAKS / SCAN ADMIN-SHELL COMPOSITION / SWEEP HTML TYPOS / AUDIT COMPONENT ANATOMY / RUN APP-SHELL QA. Triggers on \"run dogfood sweep\", \"scan component pages\", \"find broken demos\", \"visual-QA all components\", \"audit native primitive leaks\", \"find raw <button> usage\", \"audit admin-shell composition\", \"shell composition audit\", \"sweep html typos\", \"audit card header anatomy\", or scheduled dogfood-* CRON routines. Does NOT trigger for: composing UI screens (use adia-ui-kit), authoring primitives (use adia-ui-authoring), cutting releases (use adia-ui-release).",
|
|
5
|
-
"status": "stable",
|
|
6
|
-
"authors": ["kim.granlund"],
|
|
7
|
-
"tags": [
|
|
8
|
-
"dogfood",
|
|
9
|
-
"audit",
|
|
10
|
-
"visual-qa",
|
|
11
|
-
"static-analysis",
|
|
12
|
-
"headless-chromium",
|
|
13
|
-
"native-primitive-leak",
|
|
14
|
-
"admin-shell",
|
|
15
|
-
"html-typo",
|
|
16
|
-
"anatomy-sweep"
|
|
17
|
-
],
|
|
18
|
-
"peer_skills": [
|
|
19
|
-
"adia-ui-kit",
|
|
20
|
-
"adia-ui-authoring",
|
|
21
|
-
"adia-ui-release"
|
|
22
|
-
],
|
|
23
|
-
"absorbs": [],
|
|
24
|
-
"files": [
|
|
25
|
-
"SKILL.md",
|
|
26
|
-
"skill.json",
|
|
27
|
-
"CHANGELOG.md",
|
|
28
|
-
"README.md",
|
|
29
|
-
"analyze.mjs"
|
|
30
|
-
]
|
|
31
|
-
}
|
|
File without changes
|
|
File without changes
|