Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
122 commits
Select commit Hold shift + click to select a range
23529b0
ecosystem CI scaffolding + super-rentals triage
johanrd May 7, 2026
97d1da4
triage: ember-modifier (TP=0) + ember-simple-auth (TP=6 FP=0)
johanrd May 7, 2026
5905aa7
triage: ember-a11y-testing (TP-intentional=4 FP-plugin=4)
johanrd May 7, 2026
9d9e357
triage: ember-primitives (TP=3 stylistic=5 strictness=1 FP-plugin=4)
johanrd May 7, 2026
da7a59b
triage: aria-voyager (intentional=13 — all `<div role="listbox">`)
johanrd May 7, 2026
ee22d8e
triage: ember-power-select (TP=27 stylistic=6 FP-plugin-suspect=1)
johanrd May 7, 2026
382eb40
triage: limber (TP=28 stylistic=10 strictness=1 FP-plugin=0)
johanrd May 7, 2026
7d1f9da
triage: ember-website (TP=17 stylistic=163 FP-plugin=98)
johanrd May 7, 2026
9384572
triage: hds-design-system (TP=~52 stylistic=9 FP-plugin=~217)
johanrd May 7, 2026
3495fcd
triage: add Phase 1 summary to FP-FIX-REPORT
johanrd May 7, 2026
cdab9da
add overlap discussion in readme and add an ECOSYSTEM-OVERLAP.md
johanrd May 7, 2026
bf4fce1
Merge branch 'fix/fp-htmlelement-generic-fallback' into combined-fp-f…
johanrd May 7, 2026
ea6b6d3
Merge branch 'fix/fp-blank-component-yield' into combined-fp-fixes
johanrd May 7, 2026
6bfd91b
Merge branch 'fix/fp-img-splat-required-attrs' into combined-fp-fixes
johanrd May 7, 2026
9508c34
Merge branch 'fix/fp-toc-satisfies-element' into combined-fp-fixes
johanrd May 7, 2026
eecc83b
Merge branch 'fix/fp-yielded-curried-component' into combined-fp-fixes
johanrd May 7, 2026
8128fda
Merge branch 'fix/fp-classic-addon-hbs-resolution' into combined-fp-f…
johanrd May 7, 2026
d05ee5b
Merge branch 'fix/fp-arg-bound-required-attrs' into combined-fp-fixes
johanrd May 7, 2026
58f999c
Merge branch 'fix/fp-yield-only-form-fieldset' into combined-fp-fixes
johanrd May 7, 2026
a86ce53
ecosystem: add 7 new targets (ember-concurrency, warp-drive, ember-in…
johanrd May 7, 2026
a0f3f07
Merge branch 'combined-fp-fixes' into ecosystem-ci
johanrd May 7, 2026
5c1a99c
ecosystem: re-baseline against post-fix plugin + seed 7 new targets
johanrd May 7, 2026
d67d537
Merge branch 'fix/fp-yield-only-form-fieldset' into combined-fp-fixes
johanrd May 7, 2026
35149c9
Merge branch 'combined-fp-fixes' into ecosystem-ci
johanrd May 7, 2026
61af30a
ecosystem: hds form/index.gts wcag/h32 cleared after directive fix
johanrd May 7, 2026
7aa2b20
recommended: disable noisy stylistic rules; demote prefer-native-element
johanrd May 7, 2026
a2b1f4b
Merge branch 'combined-fp-fixes' into ecosystem-ci
johanrd May 7, 2026
239a8f4
ecosystem: re-baseline after :recommended rule-trim
johanrd May 7, 2026
9b39389
ecosystem: record severity (error|warning) on each baseline finding
johanrd May 7, 2026
e2753d5
ecosystem: drop noise-only targets (aria-voyager, ember-resources, em…
johanrd May 7, 2026
425037b
ecosystem: drop ember-a11y-testing — intentional violations, not regr…
johanrd May 7, 2026
2692d28
Invalidate disk cache when plugin source files change
johanrd May 7, 2026
8071596
Merge branch 'fix/fp-htmlelement-generic-fallback' into combined-fp-f…
johanrd May 7, 2026
e1c166e
Merge branch 'fix/fp-blank-component-yield' into combined-fp-fixes
johanrd May 7, 2026
28d55e9
Merge branch 'fix/fp-img-splat-required-attrs' into combined-fp-fixes
johanrd May 7, 2026
2a98e9d
Merge branch 'fix/fp-toc-satisfies-element' into combined-fp-fixes
johanrd May 7, 2026
9e0a6f5
Merge branch 'fix/fp-yielded-curried-component' into combined-fp-fixes
johanrd May 8, 2026
b42bb57
Merge branch 'fix/fp-classic-addon-hbs-resolution' into combined-fp-f…
johanrd May 8, 2026
c18c90e
Merge branch 'fix/fp-arg-bound-required-attrs' into combined-fp-fixes
johanrd May 8, 2026
6089b30
Merge branch 'fix/fp-yield-only-form-fieldset' into combined-fp-fixes
johanrd May 8, 2026
3c6283b
Merge branch 'fix/cache-invalidate-on-plugin-source-change' into comb…
johanrd May 8, 2026
eff8171
Merge branch 'combined-fp-fixes' into ecosystem-ci
johanrd May 8, 2026
ce48736
ecosystem: re-baseline with fresh caches + latest fix tips
johanrd May 8, 2026
2a5ea54
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
3d1e9a0
ecosystem: re-baseline on top of PR #21 (heuristic + classic-by-name)
johanrd May 8, 2026
cec8c2e
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
63abcd0
ecosystem: clear 2 ResponsiveImage FPs from ember-website baseline
johanrd May 8, 2026
9a571db
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
33d40b6
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
3762854
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
cdd0ed1
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
7d96e89
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 8, 2026
d464e81
Extend wcag/h32, wcag/h71, and form-submit suppression
johanrd May 9, 2026
eb7ae0e
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 9, 2026
f79b0fa
Dual-tag substitution: prefer yield-nearest-ancestor over outer wrapper
johanrd May 9, 2026
909643a
Merge branch 'fix/fp-classic-resolver-by-name-hbs' into ecosystem-ci
johanrd May 9, 2026
41e05a8
Hook-time setAttribute fallbacks for narrow Glimmer-attr slot cases
johanrd May 9, 2026
8997acf
Address Copilot review (round 6)
johanrd May 9, 2026
fa2ff3d
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 9, 2026
3089365
ecosystem: re-baseline against PR #21 tip (round 6)
johanrd May 9, 2026
5c95cba
Suppress element-permitted-* on transparent-curried-child / structura…
johanrd May 9, 2026
de38093
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 9, 2026
8723fd6
ecosystem: re-baseline after case (C) suppression (HDS 276→258, -18)
johanrd May 9, 2026
50cf376
Bail yield-ancestor resolution when template has multi-distinct yield…
johanrd May 9, 2026
8194c7b
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 9, 2026
6b25ad1
ecosystem: re-baseline after multi-yield bail (HDS 258→299)
johanrd May 9, 2026
affd09c
Skip leaf-fallback resolution for multi-template files
johanrd May 9, 2026
49e59e5
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 9, 2026
8bf5d11
ecosystem: re-baseline after multi-template-file fix (limber -5)
johanrd May 9, 2026
a9f8641
Hook-time setAttribute for substituted <button> + multi-template root…
johanrd May 9, 2026
df6fc2a
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 9, 2026
b9af7d7
ecosystem: re-baseline after button-type + multi-template fixes (HDS …
johanrd May 9, 2026
cd175ad
Anchor-aware aria-* injection: drop aria-* when role can't fit
johanrd May 9, 2026
0bf855a
ecosystem: re-baseline after anchor-aware aria-* injection (HDS -14)
johanrd May 9, 2026
eb0212d
Reject DynamicValue placeholder from `isLiteralSafeForAttr`
johanrd May 9, 2026
a4e423b
Backfill regression tests for prior PR #21 commits
johanrd May 9, 2026
b04c5f0
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
01c7b98
Polymorphic-tag chain trace via Glimmer (element ...) helper
johanrd May 10, 2026
12e4276
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
b3787ff
Narrow .d.ts → .gts mapping to polymorphic chain only
johanrd May 10, 2026
5be89e7
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
f327688
Polymorphic-tag chain trace: extract from compiled .js via TS parser
johanrd May 10, 2026
86407e7
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
bd1f2a3
ecosystem: re-baseline after polymorphic chain trace via TS (HDS -10)
johanrd May 10, 2026
b140bee
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
91b7fa7
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
fe2b17c
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
2dd1dd8
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
3c2c525
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
1df193c
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
d7467a5
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 10, 2026
644d0e0
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
ec427b3
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
4a755ff
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
7c73436
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
3823a83
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
35eea4b
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
9ffd3e7
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
cf015e3
ecosystem: re-baseline HDS (-10) + limber (+6) after FP-resolver work
johanrd May 11, 2026
677e54b
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
68fbae5
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
f174457
ecosystem: re-baseline HDS for 8 line-position shifts from lowercase-…
johanrd May 11, 2026
5d50341
Merge remote-tracking branch 'origin/fix/fp-classic-resolver-by-name-…
johanrd May 11, 2026
cf9b6d4
run: stop counting no-template .gts files as "from cache"
johanrd May 11, 2026
246ce40
Merge branch 'main' into ecosystem-ci
johanrd May 11, 2026
dda255a
add severity: 'errror' to run.js
johanrd May 12, 2026
c32a2c6
Merge branch 'main' into ecosystem-ci
johanrd May 12, 2026
029894f
Address PR #24 Copilot review
johanrd May 12, 2026
0622a32
Address second pass of PR #24 Copilot review
johanrd May 12, 2026
958e5ca
Merge branch 'main' into ecosystem-ci
johanrd May 19, 2026
346945d
Merge branch 'main' into ecosystem-ci
johanrd May 20, 2026
eaf8c9e
Fix: force HVE_GLINT='0' (not delete) when target opts out of Glint
johanrd May 20, 2026
d81f43b
Fix #38 (partial): suppress technique-rule FPs per-element, not file-…
johanrd May 20, 2026
3248fa1
Add regression tests for invalid-markup patterns unmasked by fix/38
johanrd May 20, 2026
79eb1e5
Address PR #41 Copilot review
johanrd May 20, 2026
f98e9b7
Address PR #41 Copilot review (round 2)
johanrd May 20, 2026
28db028
Address PR #41 Copilot review (round 3)
johanrd May 20, 2026
35304b5
Address PR #41 Copilot review (round 4): drop dead disableForRules
johanrd May 20, 2026
1fe83c4
Merge branch 'fix/38' into ecosystem-ci
johanrd May 20, 2026
9afe121
Merge branch 'main' into ecosystem-ci
johanrd May 20, 2026
b534081
ecosystem: re-baseline after fix/38 (per-element technique-rule suppr…
johanrd May 21, 2026
107e296
ecosystem CI: corepack enable so package-manager-pinned targets install
johanrd May 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions .github/workflows/ecosystem.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
name: ecosystem

on:
pull_request:
branches: [main, master]
paths:
- '*.ts'
- 'lib/**'
- 'ecosystem/**'
- 'package.json'
- 'pnpm-lock.yaml'
- 'tsconfig.json'
- '.github/workflows/ecosystem.yml'
workflow_dispatch:

# Cancel in-flight ecosystem runs when a PR force-pushes — clones are
# expensive and the only result we care about is the latest one.
concurrency:
group: ecosystem-${{ github.ref }}
cancel-in-progress: true

jobs:
ecosystem:
runs-on: ubuntu-latest
# Generous because targets get cloned + their deps installed for Glint
# type-aware extraction. First run is the slow path (cold caches);
# subsequent runs hit the pnpm/npm store cache and are much faster.
timeout-minutes: 60
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4

- uses: actions/setup-node@v4
with:
node-version: '22'
cache: pnpm

Comment thread
johanrd marked this conversation as resolved.
# Targets pin their package manager via package.json#packageManager
# (e.g. cardstack-ui-components → yarn). Corepack routes the `yarn`
# / `pnpm` shims to the pinned version instead of the runner's
# global yarn 1.x — without it, `installDeps` falls back to
# no-Glint validation for those targets and the baseline silently
# loses Glint-resolved findings.
- run: corepack enable

- run: pnpm install --frozen-lockfile

Comment thread
johanrd marked this conversation as resolved.
# Cache target repo clones AND their installed node_modules across runs.
# Keyed on targets.json so any SHA bump invalidates the cache and forces
# a fresh fetch + reinstall (we want the baseline-vs-actual diff to
# reflect the pinned SHA, not whatever was cached from a previous run).
# The pnpm store itself is cached by setup-node's `cache: pnpm` (above)
# against the current repo's lockfile, so target installs reuse the same
# content-addressed store transparently.
- name: Cache target clones + node_modules
uses: actions/cache@v4
with:
path: ecosystem/.cache
key: ecosystem-clones-${{ hashFiles('ecosystem/targets.json') }}

- name: Run ecosystem checks
run: pnpm run ecosystem:check
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ node_modules/
dist/
*.log
.DS_Store
ecosystem/.cache/
.claude/
278 changes: 278 additions & 0 deletions ECOSYSTEM-OVERLAP.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ templates/admin.gts:18: error [input-attributes] Attribute "readonly" not allowe
| `eslint-plugin-ember` / `ember-template-lint` | Is this idiomatic Ember? Invocation style, reactivity, modifier API, built-in components, plus JS/TS-side rules from eslint-plugin-ember and some overlap of HTML spec and ARIA rules |
| `html-validate-ember` (this plugin) | Is the rendered HTML spec-correct and accessible? — content model, ARIA / WCAG, form correctness, attribute validity, duplicate IDs, unique landmarks. |

See [ECOSYSTEM-OVERLAP.md](https://github.com/johanrd/html-validate-ember/blob/main/ECOSYSTEM-OVERLAP.md)

---

## Inline errors in VS Code
Expand Down
65 changes: 65 additions & 0 deletions ecosystem/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Ecosystem CI

Runs `html-validate-ember` against pinned snapshots of public Ember repos and diffs the findings against committed baselines. A non-empty diff fails the workflow.

## What's here

- `targets.json` — the list of pinned repos (owner/repo + SHA + globs + per-target options).
- `run.ts` — the runner. Clones, installs deps, validates with Glint, diffs.
- `baselines/<name>.json` — committed expected output per target. The runner refuses to diff if the baseline's pinned SHA disagrees with `targets.json`.

Cached clones (with their installed `node_modules`) live in `.cache/` (gitignored).

## Glint

By default each target is validated with Glint on (`HVE_GLINT=1`). That requires the target's deps installed locally — Glint resolves `@glint/ember-tsc` and the target's TypeScript types from the target's own `node_modules`. The runner detects the package manager from the lockfile and installs with `--ignore-scripts`. If install fails the runner falls back to no-Glint validation for that target and prints a warning rather than failing the run.

Per-target opt-out: set `"glint": false` on a target in `targets.json` (e.g., for repos where install doesn't work cleanly or isn't worth the time).

## Local commands

```bash
npm run ecosystem:check # all targets, fail on diff
npm run ecosystem:check -- --target=limber # just one
npm run ecosystem:check -- --target=a,b,c # subset
npm run ecosystem:check -- --no-clone # skip clone/fetch (faster, uses .cache)
npm run ecosystem:update # rewrite all baselines
npm run ecosystem:update -- --target=foo # rewrite one
```

## When to update a baseline

You're working on the plugin and a real-world target's findings changed. That's the signal — the diff is the value statement of the change. Inspect the `added:` and `removed:` blocks in the runner output:

- **Removed findings** (fewer reports) — usually a fix landed; a FP got eliminated. Good. Update the baseline in the same PR; reviewer sees what disappeared.
- **Added findings** (new reports) — either the plugin now catches something it didn't before (good — explain in the PR), or it's a regression (don't update the baseline; fix it).

The runner's diff output is the input to the PR description. Don't paraphrase it; paste the relevant lines.

## Bumping target SHAs

Manual for now. Re-fetch the default branch's HEAD, update `ref` in `targets.json`, run `npm run ecosystem:update -- --target=<name>`, commit baseline + targets.json together. The diff in the PR shows what changed in the target's templates between the old and new SHA — those are upstream-side changes, not plugin-side, but they're still worth reading: they're candidates for issue drafts to file with the upstream repo.

If a baseline is committed against a different SHA than `targets.json`, the runner refuses to diff (apples-to-oranges) and asks you to re-baseline. This guards against forgetting the second commit.

## Triage flow (separate from CI)

The committed baselines contain a mix of real bugs, plugin FPs, and html-validate rules that fire on legitimate Ember patterns. Triaging them is *not* what CI does — CI only catches regressions. Triage is a manual, batched activity that produces:

1. **Plugin fixes** for the FPs (which then show up as `removed:` in the next baseline update).
2. **Issue drafts** for the real bugs in the target repos (one per repo, batched, you file).

The expected workflow is to triage one target at a time, file an issue with the upstream repo if findings warrant it, and let the natural flow of plugin fixes update baselines over time.

## Adding a target

1. Pick a repo with non-trivial templates (`.gts`/`.gjs`/`.hbs`).
2. Probe its layout: `git clone --depth=1 --filter=blob:none <url> /tmp/probe && find /tmp/probe -name '*.gts' -o -name '*.gjs' -o -name '*.hbs' | head`.
3. Pin the current default-branch SHA: `git ls-remote <url> HEAD`.
4. Add a target entry. Globs should cover *real* templates (addon source + docs/test apps), not test fixtures (those often contain intentionally broken HTML for integration tests).
5. `npm run ecosystem:update -- --target=<name>` to seed the baseline.
6. Commit `targets.json` and `baselines/<name>.json` together.

## Why bake findings into the baseline rather than gate on zero findings

These targets have hundreds of real-world findings — some genuine, some plugin FPs, some debatable a11y choices. Refusing to merge any plugin change that doesn't drive findings to zero is impractical (the targets are out of our control). Refusing changes that *introduce* new findings is tractable. So the baseline is "current state", and the CI signal is "did this PR change real-world output, and is the change intentional?".
78 changes: 78 additions & 0 deletions ecosystem/baselines/cardstack-ui-components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
{
"ref": "589ad84bdf3afe4f35628f70497d720b9a89c193",
"fileCount": 31,
"findings": [
{
"file": "app/templates/components/cta.hbs",
"line": 1,
"column": 2,
"severity": "error",
"ruleId": "no-implicit-button-type",
"message": "<button> is missing recommended \"type\" attribute"
},
{
"file": "app/templates/components/emails.hbs",
"line": 5,
"column": 8,
"severity": "error",
"ruleId": "no-implicit-button-type",
"message": "<button> is missing recommended \"type\" attribute"
},
{
"file": "app/templates/components/file-upload-dialog.hbs",
"line": 20,
"column": 71,
"severity": "error",
"ruleId": "element-permitted-content",
"message": "<div> element is not permitted as content under <span>"
},
{
"file": "app/templates/components/file-upload-dialog.hbs",
"line": 21,
"column": 101,
"severity": "warning",
"ruleId": "prefer-native-element",
"message": "Prefer to use the native <button> element"
},
{
"file": "app/templates/components/password-field/visibility-toggle.hbs",
"line": 1,
"column": 2,
"severity": "error",
"ruleId": "no-implicit-button-type",
"message": "<button> is missing recommended \"type\" attribute"
},
{
"file": "app/templates/components/phone-number-fields.hbs",
"line": 10,
"column": 8,
"severity": "error",
"ruleId": "no-implicit-button-type",
"message": "<button> is missing recommended \"type\" attribute"
},
{
"file": "app/templates/components/street-addresses.hbs",
"line": 9,
"column": 8,
"severity": "error",
"ruleId": "no-implicit-button-type",
"message": "<button> is missing recommended \"type\" attribute"
},
{
"file": "tests/dummy/app/templates/examples/configurator.hbs",
"line": 6,
"column": 75,
"severity": "error",
"ruleId": "no-implicit-input-type",
"message": "<input> is missing recommended \"type\" attribute"
},
{
"file": "tests/dummy/app/templates/examples/configurator.hbs",
"line": 7,
"column": 75,
"severity": "error",
"ruleId": "no-implicit-input-type",
"message": "<input> is missing recommended \"type\" attribute"
}
]
}
Loading
Loading