Files
whynot-design/CONSUMING.md
tegwick 2de30beb7b
Some checks failed
ci / check (push) Has been cancelled
ci / release (push) Has been cancelled
feat(consumer): versioned IR manifest + drift-check (WHYNOT-WP-0003 T03-T07,T09)
Make ir/ the unit of versioned downstream consumption so consuming repos can
pin a version, inspect it, and follow changes at their own pace.

- T03 ir/manifest.json: per-version inventory + diff anchor with deterministic
  sha256-over-canonicalised-JSON hashes; no-churn generatedAt; manifest schema.
- T07 ir/INDEX.md: human-readable catalog generated by make ir.
- T04 .whynot-design.lock sync-point format + lock schema.
- T05 npx @whynot/design drift: consumer drift-check (bin entry), exit 0/2/3,
  --json/--update/--manifest/--version/--lock.
- T06 CONSUMING.md guide + examples/consumer-fixture/ runnable demo; README +
  MultiFrameworkSupport cross-links; fix README version pin (@0.3.0 not @v0.3.0).
- T09 CONSUMER_CONTRACT_PARITY.md design-only note (live-UI parity deferred).

T02 (publish) and T08 (showcase, blocked on WP-0002 T11) remain wait. Repo stays
in dev mode; no outward publish performed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 19:35:45 +02:00

151 lines
5.0 KiB
Markdown

# Consuming whynot-design from another repo
whynot-design is the **upstream visual reference** for other repos. It is a
development reference and demo platform — it does not run as a production
workload and handles no critical data. Consuming repos build their production
UIs *from* it, and follow up on changes **at their own pace** — they are never
force-synced.
A consumer tracks the **IR** (`ir/`), not the Lit internals. The IR is the
technology-neutral contract: per-component contracts (`ir/components/*.json`),
W3C-DTCG tokens (`ir/tokens.json`), exemplars, and the version anchor
`ir/manifest.json`. Three moves make this work:
1. **Pin** a version — the package + your lockfile.
2. **Inspect** it — `ir/INDEX.md` (browsable) + `ir/manifest.json` (machine).
3. **Get a grip on changes**`npx @whynot/design drift`.
This is the inverse of whynot-design's own upstream machinery
(`Claude Design → designbook/ → ir/`), now pointed downstream
(`ir/ → your repo`). It is **one-way**: you read the IR; you never write back.
---
## 1. Pin a version
`@whynot/design` is published to the coulomb Gitea npm registry. Pin an exact
tagged version; your lockfile becomes the real pin.
```bash
# .npmrc in your repo (see PUBLISHING.md for the read-token routing)
# @whynot:registry=https://gitea.coulomb.social/api/packages/coulomb/npm/
npm i @whynot/design@0.3.0 lit
```
`lit` is a **peer dependency** — install it alongside so your bundler dedupes to
a single `lit` instance.
## 2. Inspect what you pinned
No clone, no build needed:
- **`node_modules/@whynot/design/ir/INDEX.md`** — human-readable catalog: every
component, its tag, props/variants/slots/events, and a link to its exemplar.
- **`node_modules/@whynot/design/ir/manifest.json`** — the machine inventory:
`designVersion`, a `tokensHash`, and a content `hash` per component.
## 3. Adopt a sync-point
Record which IR state your repo has reconciled against. Run once, in your repo root:
```bash
npx @whynot/design drift --update
```
This writes **`.whynot-design.lock`** — commit it. It is the consumer-side mirror
of whynot-design's own `designbook/.design-sync.json`.
### `.whynot-design.lock` format
```json
{
"designVersion": "0.3.0",
"adoptedAt": "2026-06-27T17:31:08.640Z",
"manifestSchemaVersion": "1.0.0",
"manifestHashes": {
"tokens": "sha256:426f565a9ce6c36f",
"components": {
"Button": "sha256:4a32713049e433dd",
"TopNav": "sha256:32ebc6e46db38f93"
}
}
}
```
| field | meaning |
| --- | --- |
| `designVersion` | the `@whynot/design` version you adopted |
| `adoptedAt` | when you adopted it (first adopt, or last `drift --update`) |
| `manifestSchemaVersion` | the manifest's shape version; a mismatch warns that hashes may not be directly comparable |
| `manifestHashes.tokens` | adopted value of the manifest `tokensHash` |
| `manifestHashes.components` | adopted content hash per component |
**Lifecycle:** created on first `drift --update`, advanced only by a later
`drift --update`. Nothing else writes it. Schema: `ir/schema/lock.schema.json`.
## 4. Follow up at your own pace
When you bump `@whynot/design` (or just want to know what moved), run:
```bash
npx @whynot/design drift
```
It compares your adopted `.whynot-design.lock` against the installed package's
`ir/manifest.json` and reports **added / changed / removed** components plus
whether **tokens** changed:
```
whynot-design drift
adopted: 0.3.0 (2026-06-27T17:31:08.640Z)
target: 0.4.0 (2026-07-10T09:02:11.400Z)
Tokens: changed
Components: +1 added · ~1 changed · -0 removed · 11 total
+ Banner
~ Button
Drift detected vs your adopted sync-point.
Adopt this version: npx @whynot/design drift --update
```
Then, when *you* are ready, review the changed contracts in `ir/INDEX.md`, update
your UI, and adopt the new sync-point:
```bash
npx @whynot/design drift --update
```
### Exit codes (CI-friendly)
Mirrors the adapter contract (`adapters/ADAPTER_CONTRACT.md`):
| code | meaning |
| --- | --- |
| `0` | in sync — your lock matches the target |
| `2` | usage / config error (bad flag, missing/invalid manifest or lock) |
| `3` | **drift detected** — something changed since your sync-point |
Add `--json` for automation. Useful flags: `--manifest <path>` (diff against an
explicit manifest, e.g. a fetched newer version on disk), `--version <x.y.z>`
(assert the resolved manifest is that version — guards against the wrong install),
`--lock <path>` (non-default lock location).
> **No network, no writes to the package.** `drift` reads only the
> already-installed package + your lock, and the only file it ever writes is your
> repo's `.whynot-design.lock`.
---
## Try the full loop now
A copy-pasteable fixture lives at
[`examples/consumer-fixture/`](./examples/consumer-fixture/) — it exercises
pin → inspect → drift → update against a fixed version without needing a real
install. See its `README.md`.
See also: [`README.md`](./README.md) *Tracking whynot-design* ·
[`MultiFrameworkSupport.md`](./MultiFrameworkSupport.md) ·
[`ir/SCHEMA.md`](./ir/SCHEMA.md).