feat(consumer): versioned IR manifest + drift-check (WHYNOT-WP-0003 T03-T07,T09)
Some checks failed
ci / check (push) Has been cancelled
ci / release (push) Has been cancelled

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>
This commit is contained in:
2026-06-27 19:35:45 +02:00
parent 11684f40f3
commit 2de30beb7b
18 changed files with 1042 additions and 7 deletions

View File

@@ -0,0 +1,48 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://whynot.design/ir/schema/lock.schema.json",
"title": "whynot-design — Consumer Sync-Point Lock",
"description": ".whynot-design.lock lives in a CONSUMING repo (not in whynot-design) and records which IR state that repo has adopted. It is the consumer-side mirror of designbook/.design-sync.json. Created on first adopt and advanced only by `whynot-design drift --update`. The drift check compares this against a target ir/manifest.json to report what changed since the consumer's adopted sync-point.",
"type": "object",
"required": ["designVersion", "adoptedAt", "manifestSchemaVersion", "manifestHashes"],
"additionalProperties": false,
"properties": {
"designVersion": {
"type": "string",
"description": "The @whynot/design version whose manifest was adopted (e.g. matches the installed package / tag vX.Y.Z)."
},
"adoptedAt": {
"type": "string",
"format": "date-time",
"description": "ISO-8601 timestamp this sync-point was adopted (first adopt or last `drift --update`)."
},
"manifestSchemaVersion": {
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
"description": "The adopted manifest's schemaVersion. If a target manifest has a different schemaVersion the drift check warns that hashes may not be directly comparable."
},
"manifestHashes": {
"type": "object",
"description": "The subset of ir/manifest.json the consumer has reconciled against.",
"required": ["tokens", "components"],
"additionalProperties": false,
"properties": {
"tokens": {
"$ref": "#/$defs/hash",
"description": "Adopted value of the manifest's tokensHash."
},
"components": {
"type": "object",
"description": "Map of component name → adopted content hash.",
"additionalProperties": { "$ref": "#/$defs/hash" }
}
}
}
},
"$defs": {
"hash": {
"type": "string",
"pattern": "^sha256:[0-9a-f]{16}$"
}
}
}