` + `aria-hidden` — ARIA string attribute
+
+ARIA attributes are string-valued, but Glimmer's bare-mustache form applies the same falsy-coercion as for boolean HTML attributes (omit on `false`/`null`/`undefined`). Bare-string and concat forms render the value literally — concat does **not** coerce to a boolean here. The "At runtime (per ARIA spec)" column derives whether the element is hidden from assistive tech: `aria-hidden="true"` is hidden; `aria-hidden="false"` is visible; `aria-hidden=""` (and the implied default) is contested.
+
+| ID | Source | outerHTML | IDL `ariaHidden` | hasAttr | At runtime (ARIA) |
+| --- | --------------------------------------- | --------------------------------- | ---------------- | ------- | ---------------------------------------------- |
+| h1 | `
` | `
` | `""` | `true` | **contested** (empty value) |
+| h2 | `
` | `
` | `""` | `true` | **contested** |
+| h3 | `
` | `
` | `"true"` | `true` | **hidden** |
+| h4 | `
` | `
` | `"false"` | `true` | **visible** |
+| h5 | `
` | `
` | `""` | `true` | **contested** (rendered empty, _not_ `"true"`) |
+| h6 | `
` | `
` | `null` | `false` | **visible** (default) |
+| h7 | `
` | `
` | `"true"` | `true` | **hidden** |
+| h8 | `
` | `
` | `"false"` | `true` | **visible** |
+| h9 | `
` | `
` | `null` | `false` | **visible** |
+| h10 | `
` | `
` | `null` | `false` | **visible** |
+| h11 | `
` | `
` | `""` | `true` | **contested** |
+| h12 | `
` | `
` | `"true"` | `true` | **hidden** |
+| h13 | `
` | `
` | `"false"` | `true` | **visible** |
+| h14 | `
` | `
` | `"true"` | `true` | **hidden** |
+| h15 | `
` | `
` | `"false"` | `true` | **visible** |
+
+**Lint truth for `aria-hidden`:** the rule depends on the value, not just presence. Notable differences from boolean attrs: bare `{{true}}` renders as `aria-hidden=""` (contested, not `"true"`); concat `="{{false}}"` renders as `aria-hidden="false"` (visible — _not_ IDL-coerced like boolean attrs).
+
+### `
` + `tabindex` — numeric attribute
+
+Falsy-coercion (`false`/`null`) omits, like boolean attrs. Numeric and string-numeric values render the literal. IDL `tabIndex` returns `-1` when no attribute is set (the default for non-focusable elements), so `hasAttr` is the cleaner signal for "tabindex is set" than checking `tabIndex`.
+
+| ID | Source | outerHTML | IDL `tabIndex` | hasAttr | Effective |
+| --- | -------------------------------- | --------------------------- | -------------- | ------- | ----------- |
+| t1 | `
` | `
` | `0` | `true` | tabindex 0 |
+| t2 | `
` | `
` | `-1` | `true` | tabindex -1 |
+| t3 | `
` | `
` | `1` | `true` | tabindex 1 |
+| t4 | `
` | `
` | `0` | `true` | tabindex 0 |
+| t5 | `
` | `
` | `0` | `true` | tabindex 0 |
+| t6 | `
` | `
` | `-1` (default) | `false` | not set |
+| t7 | `
` | `
` | `-1` (default) | `false` | not set |
+
+**Lint truth for `tabindex`:** rules that care about the value should extract the literal from the AST (the value is preserved as-written across all bare and concat literal forms). Rules that care about presence should check that the source is not bare `{{false}}` / `{{null}}` (and by inference `{{undefined}}`, not tested).
+
+### `
` + `autocomplete` — string attribute (not boolean-coerced)
+
+A regular string attribute. Glimmer's bare-mustache **does not** apply falsy-coercion here — `autocomplete={{false}}` renders as `autocomplete="false"` (kept). This is the key difference from boolean HTML attrs and from `aria-*`. The IDL `el.autocomplete` canonicalizes the attribute value (returns `""` for invalid tokens), so it differs from `getAttribute('autocomplete')` for non-spec values.
+
+| ID | Source | outerHTML | IDL `autocomplete` | hasAttr | attrValue |
+| --- | ------------------------------------ | ------------------------------ | -------------------- | ------- | --------- |
+| i1 | `
` | `
` | `"off"` | `true` | `"off"` |
+| i2 | `
` | `
` | `"off"` | `true` | `"off"` |
+| i3 | `
` | `
` | `"off"` | `true` | `"off"` |
+| i4 | `
` | `
` | `""` (canonicalized) | `true` | `"false"` |
+| i5 | `
` | `
` | `""` (canonicalized) | `true` | `"false"` |
+
+**Lint truth for `autocomplete`:** rules should check `getAttribute('autocomplete')` (or its AST equivalent) against the spec's valid token list, not the IDL property. The bare-mustache `{{false}}` form will give a literal `"false"` value — almost certainly an authoring bug worth flagging.
+
+### Cross-attribute observations
+
+- **Glimmer's bare-mustache "boolean coercion" list.** For `muted` (HTML boolean), `disabled` (HTML boolean), `aria-hidden` (ARIA string), and `tabindex` (numeric), bare `{{false}}` / `{{null}}` / `{{undefined}}` (and `{{0}}` for `muted`) cause the attribute to be **omitted**. For `autocomplete` (plain string), bare `{{false}}` renders as `autocomplete="false"`. So Glimmer applies boolean-coercion to a known set — at minimum HTML boolean attrs, ARIA attrs, and numeric attrs. Plain string attrs do not get coerced.
+- **Bare-mustache string literals never coerce.** `attr={{"false"}}`, `attr={{"true"}}`, `attr={{""}}` always render as `attr="
"` for every attribute kind tested. The literal `"false"` is JS-truthy and gets passed through.
+- **Bare-mustache numeric `0` is in the falsy set for `muted`.** Verified for `muted` (`{{0}}` → omitted). Not yet tested for `disabled` / `aria-hidden` / `autocomplete`.
+- **Concat-mustache forks by attribute kind.** For HTML boolean attrs (`muted`, `disabled`), any concat — including `"{{false}}"`, `"{{'false'}}"`, `"x{{false}}"` — sets the IDL property to `true`, regardless of the literal value inside. For ARIA / string attrs (`aria-hidden`, `autocomplete`), concat renders the stringified value as the attribute value (no boolean coercion); `aria-hidden="{{false}}"` becomes `aria-hidden="false"` (visible).
+- **Concat is never falsy.** Across all attribute kinds tested, no concat form produces an absent attribute. Rules treating `attr="{{false}}"` as "off" are wrong for boolean attrs (it's IDL-true) and wrong for string attrs (the rendered value is `"false"`, attribute present).
+
+## To reproduce the reference table
+
+Every cell above was populated by rendering the template below in an Ember dev app and running the bundled JS console snippet to print each test case's `outerHTML` and IDL state. To re-verify (or extend with new attributes):
+
+1. Paste sections A–E into a template in your Ember dev app.
+2. Render in the browser, open devtools.
+3. Paste the JS snippet (after the template) into the console.
+4. Compare the printed output against the tables above.
+5. If anything diverges, update the changed cell, cite the new `ember-source` version in the commit, and note the change.
```hbs
{{! A.