# Adapter Contract > The contract **every** whynot stack adapter implements, so a new stack > (Vue, Angular, Svelte, plain-CSS, …) is a drop-in. Part of WHYNOT-WP-0002. > Lit (`adapters/lit/`) is the reference implementation. Django > (`adapters/django/`) predates the IR and is a hand-authored Layer-3 partial > set, not an IR adapter — it is exempt until/unless migrated. An adapter is **scaffold + drift-detect**, not full behavioural codegen: - **tokens** → fully generated (deterministic, re-run is a no-op when unchanged), - **new component** → a stub is generated (skeleton + typed inputs + a behaviour TODO), - **changed component** → a **drift report** is emitted; the hand-authored source is **never overwritten**, - **appearance** → verified against the designbook's own exemplars (parity). Behaviour stays hand-authored per stack. The adapter keeps each component's *contract and appearance* aligned; it does not own its behaviour. --- ## Inputs The sole input is the committed IR (`ir/`, produced one-way from the React designbook — see `ir/SCHEMA.md`): | Input | What the adapter reads it for | |---|---| | `ir/tokens.json` | Token generation (W3C DTCG → stack-native token form). | | `ir/components/.json` | Per-component contract: props, prop→attribute map, slots, events, variants. | | `ir/exemplars/.{html,png}` | Reference render for visual parity. | | `ir/schema/` | The schemas the adapter may re-validate inputs against. | An adapter **MUST NOT** write to `ir/`. Flow is one-way: React → IR → stacks. ## Outputs An adapter produces exactly three kinds of output: 1. **Generated artifacts** into the stack's own source tree. - Tokens: fully generated, deterministic (e.g. Lit → `src/styles/colors_and_type.css`). - New-component stubs: written to the stack's component tree, marked generated, with a behaviour `TODO`. A stub is created **only** when no hand-authored counterpart exists. 2. **A machine-readable drift report** — one file per drifted component plus a roll-up. Lit writes these to `adapters/lit/drift/.md` (human view) backed by a machine-readable summary. The report enumerates, per component: - props present in IR but missing on the element (and vice-versa), - prop→attribute mismatches, - missing / extra / renamed variants, - removed props, - **non-portable props** (`portable:false`) surfaced explicitly — never dropped. 3. **A parity result** — a single structured outcome per `make parity-` covering (a) **contract parity** (observed attributes/properties vs IR) and (b) **visual parity** (rendered component diffed against `ir/exemplars/`). ### Drift report — minimal machine shape ```json { "stack": "lit", "generatedAt": "", "irRef": "", "components": [ { "name": "Button", "status": "drift", // "ok" | "new" | "drift" | "removed" "issues": [ { "kind": "prop-missing", "prop": "tone", "detail": "in IR, absent on " }, { "kind": "attribute-mismatch","prop": "iconEnd", "expected": "icon-end", "actual": "iconend" }, { "kind": "non-portable", "prop": "renderLabel", "detail": "type=function; cannot map to attribute" } ] } ] } ``` ### Parity result — minimal machine shape ```json { "stack": "lit", "generatedAt": "", "components": [ { "name": "Button", "contract": "pass", "visual": "pass", "diffRatio": 0.0009 } ], "summary": { "total": 1, "contractFail": 0, "visualFail": 0 } } ``` ## Idempotency rules 1. **Tokens regenerate fully.** Running token generation twice on an unchanged `ir/tokens.json` yields a byte-identical file (no-op diff). 2. **Stubs are write-once.** A stub is generated only when no hand-authored source exists. Once a human has touched a component, the adapter never re-writes it — it emits drift instead. 3. **Behaviour is never overwritten.** No adapter output replaces hand-authored behaviour. The strongest action against an existing component is a drift report. 4. **Reports are overwritten, not appended.** Drift and parity outputs are snapshots of the current IR-vs-source state; each run replaces the previous. 5. **No network, no `ir/` writes.** Adapters are pure functions of `ir/` + the stack source tree. ## Exit codes (for CI) Every adapter command (`make adapt-`, `make parity-`) follows the same convention so pipelines can branch uniformly: | Code | Meaning | |---|---| | `0` | Success. Tokens/stubs generated; no drift; parity passed. | | `2` | Usage / configuration error (bad args, missing `ir/`, malformed input). | | `3` | **Drift detected.** Generation succeeded but one or more components drifted from the IR. Non-fatal by default; use to gate a review. | | `4` | **Parity failure.** A contract or visual parity check failed. | | `5` | Internal adapter error (unexpected exception). | Codes are additive in spirit but a command returns the **highest applicable** code (e.g. drift + parity failure → `4`). `make designbook-refresh` (T09) treats `3` as "stop for human drift triage" and `4` as "fail". ## Implementing a new adapter — checklist 1. Create `adapters//` with a `README.md` pointing back to this contract. 2. Implement token generation from `ir/tokens.json` → the stack's token form (full gen, deterministic). 3. Implement stub generation from `ir/components/.json` using the **prop→attribute map** (respect `attribute:false` and `portable:false`). 4. Implement drift detection against existing hand-authored sources → the drift report shape above. 5. Implement `make parity-` → the parity result shape above, reusing the Playwright harness where the stack renders to HTML. 6. Wire `make adapt-` and `make parity-`; honour the exit codes. See `adapters/lit/` for the reference implementation.