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>
51 lines
2.6 KiB
JSON
51 lines
2.6 KiB
JSON
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"$id": "https://whynot.design/ir/schema/manifest.schema.json",
|
|
"title": "whynot IR — Version Manifest",
|
|
"description": "ir/manifest.json — the per-version inventory and diff anchor. Generated only by scripts/ir-extract.mjs (make ir). Each hash is a deterministic content hash (sha256 over canonicalised JSON: keys sorted, no insignificant whitespace) so it is invariant to formatting and sensitive only to meaningful contract/token change. The consumer drift-check (WHYNOT-WP-0003 T05) compares two manifests, or a manifest against a consumer's .whynot-design.lock.",
|
|
"type": "object",
|
|
"required": ["schemaVersion", "designVersion", "generatedAt", "tokensHash", "components"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"schemaVersion": {
|
|
"type": "string",
|
|
"description": "Shape version of THIS manifest. Bumped when the manifest layout or the hashing scheme changes, so a consumer can tell a re-canonicalisation apart from a real design change.",
|
|
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$"
|
|
},
|
|
"designVersion": {
|
|
"type": "string",
|
|
"description": "The package.json version this manifest was extracted at (e.g. the semver tied to a git tag vX.Y.Z)."
|
|
},
|
|
"generatedAt": {
|
|
"type": "string",
|
|
"format": "date-time",
|
|
"description": "ISO-8601 timestamp of extraction. Reused from the prior manifest when nothing hashed changed, so a no-op `make ir` does not churn the committed file. Informational only — never part of any hash."
|
|
},
|
|
"tokensHash": {
|
|
"$ref": "#/$defs/hash",
|
|
"description": "Content hash of the full canonicalised ir/tokens.json. A single coarse hash by design (token-level diff granularity is a deferred refinement)."
|
|
},
|
|
"components": {
|
|
"type": "array",
|
|
"description": "One entry per component contract, sorted by name.",
|
|
"items": {
|
|
"type": "object",
|
|
"required": ["name", "group", "hash"],
|
|
"additionalProperties": false,
|
|
"properties": {
|
|
"name": { "type": "string", "pattern": "^[A-Z][A-Za-z0-9]*$" },
|
|
"group": { "type": "string", "description": "The component's group (atoms, chrome, …)." },
|
|
"hash": { "$ref": "#/$defs/hash", "description": "Content hash of the canonicalised ir/components/<name>.json contract." }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"$defs": {
|
|
"hash": {
|
|
"type": "string",
|
|
"pattern": "^sha256:[0-9a-f]{16}$",
|
|
"description": "Algorithm-prefixed truncated digest: 'sha256:' + first 16 hex chars of sha256(canonical JSON)."
|
|
}
|
|
}
|
|
}
|