# HM composition matrix

Auto-generated by `tools/composition_matrix.py --write-catalog`.
Do not hand-edit — update `HOSTS` / `GUESTS` / `INCOMPATIBLE` in the tool.

Stem: [`stems/host-chrome-symmetry.md`](stems/host-chrome-symmetry.md)

Run:

```bash
python packages/hatchi-maxchi/tools/composition_matrix.py --validate
python packages/hatchi-maxchi/tools/composition_matrix.py --list
```

## Hosts (chrome shells)

| id | host | kind | notes |
|----|------|------|-------|
| `drawer.form_shell` | drawer | form_shell | One method=dialog wraps chrome when body has no nested forms. |
| `drawer.exchange_shell` | drawer | exchange_shell | Scoped close forms; body is exchange target (may receive forms). |
| `dialog.form_shell` | dialog | form_shell | Dialog form_shell — same chrome parts as gallery dialog demo. |
| `dialog.exchange_shell` | dialog | exchange_shell | Dialog exchange_shell — HTMX body with scoped close forms. |

## Guests

| id | notes |
|----|-------|
| `field` | label + control + hint triad |
| `switch` | label.switch + track — not controls pill |
| `controls` | native designed checkbox/radio |
| `toggle-group` | label outside fieldset |
| `badge` | — |
| `card` | one KPI card per metric |
| `alert` | — |
| `button` | — |
| `menu` | details menu — opens inside body; light-dismiss is guest concern |
| `tabs` | selection-strip honest: buttons + panels |
| `separator` | — |
| `empty-state` | — |
| `popover` | — |
| `kbd` | — |
| `skeleton` | — |
| `nested-form` | body-local form — only exchange_shell (no nested form in form_shell) |
| `nested-dialog` | refusal probe — never nest overlay chrome in a host body |
| `command` | refusal probe — command palette is its own L1 overlay, not a guest |

## Incompatible cells (declared refusals)

| host | guest | reason |
|------|-------|--------|
| `dialog.exchange_shell` | `command` | command palette is its own L1 overlay (addressing + focus trap); not a body guest |
| `dialog.exchange_shell` | `nested-dialog` | do not nest dialog chrome inside another overlay body — open a sibling dialog |
| `dialog.form_shell` | `command` | command palette is its own L1 overlay (addressing + focus trap); not a body guest |
| `dialog.form_shell` | `nested-dialog` | do not nest dialog chrome inside another overlay body — open a sibling dialog |
| `dialog.form_shell` | `nested-form` | form_shell already wraps method=dialog — nested <form> is invalid HTML; use exchange_shell |
| `drawer.exchange_shell` | `command` | command palette is its own L1 overlay (addressing + focus trap); not a body guest |
| `drawer.exchange_shell` | `nested-dialog` | do not nest dialog chrome inside another overlay body — open a sibling dialog |
| `drawer.form_shell` | `command` | command palette is its own L1 overlay (addressing + focus trap); not a body guest |
| `drawer.form_shell` | `nested-dialog` | do not nest dialog chrome inside another overlay body — open a sibling dialog |
| `drawer.form_shell` | `nested-form` | form_shell already wraps method=dialog — nested <form> is invalid HTML; use exchange_shell |

## Compatibility grid

Legend: `Y` compatible (structural validate) · `N` incompatible (reason above)

| host \ guest | `field` | `switch` | `controls` | `toggle-group` | `badge` | `card` | `alert` | `button` | `menu` | `tabs` | `separator` | `empty-state` | `popover` | `kbd` | `skeleton` | `nested-form` | `nested-dialog` | `command` |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| `drawer.form_shell` | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | N | N | N |
| `drawer.exchange_shell` | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | N | N |
| `dialog.form_shell` | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | N | N | N |
| `dialog.exchange_shell` | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y | N | N |

## Playwright coherence subset

Browser layout checks (non-zero box, primary body colour) run for:

- `drawer.form_shell` × `field`
- `drawer.form_shell` × `switch`
- `drawer.form_shell` × `toggle-group`
- `drawer.form_shell` × `card`
- `drawer.form_shell` × `tabs`
- `drawer.form_shell` × `menu`
- `drawer.exchange_shell` × `nested-form`
- `drawer.exchange_shell` × `empty-state`
- `dialog.form_shell` × `alert`
- `dialog.exchange_shell` × `button`
