# Mode 4, Native-primitive leak: severities + annotation contract

Script: `scripts/dev/audit-native-primitive-leak.mjs` (repo-local), npm gates
`audit:native-primitive-leak{,:all,:strict}`. Flags beyond the gates:
`--strict-links` (promote `<a href>` to warning) · `--tag=button` ·
`--include=apps/genui` · `--json`. Exit 1 on ≥1 critical (any finding under
`--strict`).

Why it exists: the substrate is light-DOM; native primitives bypass theme
tokens, focus-ring discipline, accessibility patterns, and the trait registry.
Distinct from visual probe #6: that catches "right tag, forgot the CSS link";
this catches "wrong tag entirely". The forward-time authoring discipline this
enforces lives in the consumer factory's primitive-audit; this is the
backward-time check.

## Per-tag severity + built-in escape hatches

| Tag | Severity | Equivalent | NOT flagged |
|---|---|---|---|
| `<button>` | critical | `button-ui` | none, always a smell |
| `<input>` | critical | `input-ui` | `type="hidden"` / `"file"` / `"radio"` / `"checkbox"` |
| `<select>` | critical | `select-ui` | none |
| `<textarea>` | critical | `textarea-ui` | none |
| `<table>` | critical | `table-ui` | none |
| `<a href>` | info (warning w/ `--strict-links`) | `link-ui` | bare `<a name=…>` anchors |
| `<img>` | info | `image-ui` | often legit for content-loaded assets |

Skipped automatically: component showcase demos
(`packages/web-components/components/<name>/<name>.html`, they demonstrate
native usage in code blocks), anything inside `<code>`/`<pre>`/`<script>`/
`<style>`, stamp-internal usage inside the matching primitive's own directory,
and `node_modules` / `.git` / `dist` / `build`.

## Escape-hatch annotation (`INTENT_MARKERS` contract)

When native IS right (file picker, hidden field, content-loaded image), the
annotation demotes the finding to info (visible in `--all`, doesn't fail):

```html
<!-- attribute form, preferred, lives inside the opening tag -->
<input type="file" data-native-ok="file picker, no upload-ui equiv" accept=".json" />

<!-- preceding-comment form -->
<!-- native-ok: file picker, no upload-ui equivalent -->
<input type="file" accept=".json" />
```

The script scans a **200-char window before** the opening tag for
`<!-- native-ok:` or `data-native-ok=` (widened from 80 after realistic
preceding-line comments sat exactly at the 80-char boundary undetected). A
comment block over ~180 chars total can still fall outside the window, for
verbose reasons always use the attribute form; it needs no window math.

## Triage (this mode NEVER auto-fixes)

Per critical finding:

1. Genuine escape hatch the script missed? → add the annotation, re-run.
2. Real bug? → replace with the `*-ui` equivalent, preserving slot/attribute
   semantics per the attribute-api-migration convention. Every replacement is
   human-eyeballed, the attribute shape (`text=` vs slotted child) is a
   judgment call.
3. Is the replacement primitive actually registered in the consuming shell?
   Cross-check the page-trio `.contents.js` imports.

## Anti-patterns

- Don't bulk-replace `<a href>` with `link-ui`, most anchors are legitimate
  navigation; that's why `--strict-links` is opt-in, never the default.
- Don't point the scan at showcase demos or docs trees, code-block noise.
- New primitive with a clear native equivalent → add a `PROBES` entry in the
  script; consistently-legitimate native usage in one surface → extend the
  skip-list with rationale.
