Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 90 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,95 @@
# Changelog

## 0.5.0

Ledger v2: per-component ownership inside `metadata["x-bootstrap"]`
(exporter-owned, non-semantic metadata — no dspack spec change). Resolves
the two granularity limitations recorded in #13.

- **Per-entry hashes** (`ledger.components`, id → sha256): ownership of the
components section is now decided entry by entry. Tool-owned entries
refresh deterministically; a human-edited entry is preserved verbatim and
its stale recorded hash (or absence from the map) is the human-owned
signal.
- **Deletion memory (skip-and-ask)**: a ledger hash whose entry is absent
from the document is an orphan — rediscovery never silently restores the
component; it reports `deletedAwaitingDecision` and carries the hash
forward until a human resolves it.
- **Explicit `restoredConflict` resolutions**: an id that lives on as
authored sub-component vocabulary is never auto-added and its memory is
never auto-retired (that would convert an inference into ownership
state). The owner resolves it explicitly: *keep nested* (tombstone the
id and retire the hash — subsequent runs report `suppressed`, the
conflict stops), *restore top-level* (the new
`regenerateSections(existing, fresh, { restoreTopLevel: [id] })` intent:
restored from fresh as tool-owned, the nested authored representation
preserved, the orphaned hash replaced by the restored entry's own,
reported as `restoredTopLevel` with the parent named — both
representations now exist), or *leave unresolved* (no change, the
memory and the report persist). Intents that cannot be honored —
already present, tombstoned, missing from fresh — refuse the whole run
with every id named; nothing is partially honored.
- **`doNotRediscover` tombstones** (plain component ids, authored data):
rediscovery skips them unambiguously (`suppressed`); a tombstoned id that
is nonetheless present in the document reconciles as
`suppressedButPresent`.
- **Fail-closed against pre-v2 tooling**: `generated.components` (the v1
whole-section signal) is written only when every entry is tool-owned and
no tombstone or orphan exists, so any v2-active document refuses under
shipped 0.4.0's `decideRegeneration` (the whole-file path) — byte-tested
against the published package (`dspack-export-shipped` devDep). This
release also teaches `decideRegeneration` to refuse whenever a v2 ledger
carries tombstones or deletion memory, even if the components section
was hand-deleted. Honest limit: shipped 0.4.0's `regenerateSections`
predates tombstones and treats the unrecorded section as human-owned
pure-addition — it would re-add suppressed ids. That is exactly why
**ledger-v2 documents require this version or later** (the ratified
version floor rather than a 0.4.x patch).
- **`freshDelta`** on preserved human-owned entries: fresh-side facts only
(added props/variants/enum values, changed scalars, with values and
JSON-pointer-like paths), report-only, never persisted, never a merge.
Explicitly not claimed: detection of source removals inside enriched
entries (reconsidered only on real-project evidence — no telemetry).
- **Migration is automatic and byte-stable outside `x-bootstrap`**: a v1
ledger with a tool-owned components section synthesizes per-entry hashes;
a human-owned section migrates with entries unattributed, and re-adoption
reclaims any entry still byte-identical to fresh discovery. Malformed,
contradictory, or partially migrated ledgers refuse with pathed findings.
There is still no force flag anywhere.
- Regeneration report gains per-entry classifications (`added`, `refreshed`,
`unchanged`, `readopted`, `preservedEnriched`, `removedWithSource`,
`keptMissingInFresh`, `deletedAwaitingDecision`, `suppressed`,
`suppressedButPresent`, `restoredConflict`, `restoredTopLevel`) so callers
(the Studio composer) can present every decision explicitly. Deletion
memory is never auto-retired: an orphan whose id is also absent from
fresh extraction keeps its record and keeps asking (a transient
extraction absence must not turn a recorded deletion into a future
silent re-add).
- **Migration asks instead of guessing**: a v1 human-owned components
section cannot distinguish "hand-deleted" from "newly discovered since
the snapshot", so the migration run seeds deletion memory for every
fresh-only id and reports it `deletedAwaitingDecision` — nothing is
silently added across the v1→v2 boundary. Restoring is one explicit
decision (`restoreTopLevel`, or clear the memory and rediscover).
- `LEDGER_VERSION`, `ComponentReport`, and `FreshFact` are exported;
golden fixtures regenerated with v2 ledgers; the pack-and-install
boundary test now exercises orphan skip-and-ask and tombstone suppression
from the tarball.

## 0.4.0

(Entry backfilled with 0.5.0; released 2026-08-03 via PR #12.)

- Programmatic library entry: `exportProject(configPath)` (the equivalent of
`generate --config`) plus exported primitives (`sectionHash`,
`buildLedger`, `decideRegeneration`, types).
- **Section-scoped `regenerateSections(existing, fresh)`**: refreshes only
hash-matching tool-owned sections, preserves human-owned sections and
governance verbatim, adds newly discovered components into a human-owned
components section as pure additions, and refuses ledger-less documents.
No force flag.
- Pack-and-install boundary test (`npm run test:pack`) wired into CI.

## 0.3.0

The DX-3 bootstrap boundary (dspack `rfc/dx3-bootstrap-design.md`), and the
Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ across frameworks. The active adapter is chosen by an optional `framework` confi
field, or inferred from component file extensions (`.tsx/.jsx` → React, `.vue` →
Vue) with a hard error on ambiguous input.

**Status: experimental** (`0.3.0`). Config format and output details may
**Status: experimental** (`0.5.0`). Config format and output details may
still change between versions. Published to npm as
`@aestheticfunction/dspack-export`. The [handbook](docs/handbook.md) covers
the supported stack, known limitations, and troubleshooting.
Expand All @@ -32,6 +32,20 @@ generated (with content hashes) and which surfaces await authorship. The
ledger is non-semantic: it exists only so regeneration can make safe
decisions, and deleting it marks the document fully human-owned.

Since 0.5.0 the ledger is **v2**: ownership of the components section is
tracked per entry (`ledger.components`, id → hash), so `regenerateSections`
can refresh untouched entries while preserving enriched ones verbatim. A
hash whose entry was hand-deleted is deletion memory — rediscovery skips it
and asks (`deletedAwaitingDecision`) instead of silently restoring it — and
ids listed in `ledger.doNotRediscover` are never re-added. An id you
re-authored as a sub-component of another entry reports `restoredConflict`
until you decide: tombstone it to keep the nested form, or pass
`regenerateSections(existing, fresh, { restoreTopLevel: [id] })` to restore
the top-level entry alongside it. Any document using these v2 states omits
the v1 whole-section signal, so older releases' whole-file regeneration
refuses to touch it; the 0.4.0 merge path predates tombstones, which is
why ledger-v2 documents require 0.5.0 or later.

**Regeneration never destroys human-authored content.** If the output file
contains anything this tool does not own — governance blocks, edited
sections, or no ledger at all — `generate` refuses, explains why, and
Expand Down
7 changes: 6 additions & 1 deletion fixtures/dtcg-tokens-demo/dtcg-tokens-demo.dspack.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
"description": "Fixture exercising DTCG design-token-file import alongside CSS token extraction.",
"version": "1.0.0",
"metadata": {
"generatedBy": "@aestheticfunction/dspack-export@0.3.0",
"generatedBy": "@aestheticfunction/dspack-export@0.5.0",
"generatedAt": "2026-06-10T00:00:00.000Z",
"source": "fixtures/dtcg-tokens-demo",
"note": "Generated snapshot. Hand-authored sections (patterns, antiPatterns, whenToUse, accessibility, composition, constraints) are not generated; regeneration refuses to overwrite a document containing human-authored content (see metadata[\"x-bootstrap\"]).",
"x-bootstrap": {
"ledger": "2",
"spec": "0.4",
"generated": {
"tokens": "6ff5c3fd63f4ba56337320d79583982bfce66919a3552f4938e95cbd1005326c",
Expand All @@ -17,6 +18,10 @@
"themes": "8fec47475f975ca9da6b547d5bd2d8ab5ac0f1c126ce98bb0614c01f39a66dad",
"layout": "84240158ff29a8c25fd428fdf24f3409df36710fabef89086d711704bc0130c2"
},
"components": {
"button": "ec7d55f9e100fbe58ef0f9a407882592f681515d365aeba6849d797f847aad45"
},
"doNotRediscover": [],
"awaitingAuthorship": [
"categories",
"intents",
Expand Down
13 changes: 12 additions & 1 deletion fixtures/shadcn-demo/shadcn-demo.dspack.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
"description": "Demo shadcn-style design system used as the dspack-export golden fixture.",
"version": "1.0.0",
"metadata": {
"generatedBy": "@aestheticfunction/dspack-export@0.3.0",
"generatedBy": "@aestheticfunction/dspack-export@0.5.0",
"generatedAt": "2026-06-10T00:00:00.000Z",
"source": "fixtures/shadcn-demo",
"note": "Generated snapshot. Hand-authored sections (patterns, antiPatterns, whenToUse, accessibility, composition, constraints) are not generated; regeneration refuses to overwrite a document containing human-authored content (see metadata[\"x-bootstrap\"]).",
"x-bootstrap": {
"ledger": "2",
"spec": "0.4",
"generated": {
"tokens": "d031a79c0a10556c38b099145be82d26eed263713c60922efab56338aca69f15",
Expand All @@ -17,6 +18,16 @@
"themes": "46510dd813828a275bd601d0f0348934b9cd1b8b29b4cc525538472f3958173f",
"layout": "84240158ff29a8c25fd428fdf24f3409df36710fabef89086d711704bc0130c2"
},
"components": {
"badge": "8441ce8aef1597b09e3e9ad14e71e1dba5b00e519a90b7e0cfd139263d4085f6",
"button": "115d3bed70bd9ceab5338e9f1e3e379ac61f3b0c35992ee83fe7dd59e6dfb573",
"card": "3c0e8da41c1130bd245a08bea4c9287812cfe449a5e468dd03e7c3c36c459644",
"card-header": "5467b6377f5a356856ab89ba2a45120b89b2e98a1f926b842d95339b5dadcc8a",
"card-title": "b475008cd40f61288baa5bd7af9c7a947bdd0a890756ee865025b2a83f51d479",
"card-content": "6464cf3e1ef6f041a2feb434c95bd74cd6a92ef023f900d4c7b5bf5f05a573df",
"input": "e591c2b2c642c2a169fdead4a85ccc4123a76db88d7cfb918a32f9aca6677a9c"
},
"doNotRediscover": [],
"awaitingAuthorship": [
"categories",
"intents",
Expand Down
7 changes: 6 additions & 1 deletion fixtures/shadcn-v4-demo/shadcn-v4-demo.dspack.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
"description": "Tailwind v4 / @theme-style fixture for dspack-export.",
"version": "1.0.0",
"metadata": {
"generatedBy": "@aestheticfunction/dspack-export@0.3.0",
"generatedBy": "@aestheticfunction/dspack-export@0.5.0",
"generatedAt": "2026-06-10T00:00:00.000Z",
"source": "fixtures/shadcn-v4-demo",
"note": "Generated snapshot. Hand-authored sections (patterns, antiPatterns, whenToUse, accessibility, composition, constraints) are not generated; regeneration refuses to overwrite a document containing human-authored content (see metadata[\"x-bootstrap\"]).",
"x-bootstrap": {
"ledger": "2",
"spec": "0.4",
"generated": {
"tokens": "a6f0f842d11f89823929bb7e3df4d40c19357c119dd42a687e0adbd1fa8e58d0",
Expand All @@ -17,6 +18,10 @@
"themes": "fafbb44d5be2b3a7954c6cb4fb1590a669e8aa1c84eecb3ad15b421e14a0042b",
"layout": "0b92b1df3e314dcb730718fd0c087a680750c7a3a33f16f2f1c0a6d885c4c585"
},
"components": {
"button": "0af3dda0e80d46130863b999b31ebe5c30903789a8329755294cf1c3bb497cbf"
},
"doNotRediscover": [],
"awaitingAuthorship": [
"categories",
"intents",
Expand Down
11 changes: 10 additions & 1 deletion fixtures/vuetify-demo/vuetify-demo.dspack.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,26 @@
"description": "Demo Vue 3 + Vuetify 3 design system used as the dspack-export Vue golden fixture.",
"version": "1.0.0",
"metadata": {
"generatedBy": "@aestheticfunction/dspack-export@0.3.0",
"generatedBy": "@aestheticfunction/dspack-export@0.5.0",
"generatedAt": "2026-06-10T00:00:00.000Z",
"source": "fixtures/vuetify-demo",
"note": "Generated snapshot. Hand-authored sections (patterns, antiPatterns, whenToUse, accessibility, composition, constraints) are not generated; regeneration refuses to overwrite a document containing human-authored content (see metadata[\"x-bootstrap\"]).",
"x-bootstrap": {
"ledger": "2",
"spec": "0.4",
"generated": {
"tokens": "411ade9c82231a3fcb56a281e79f25d4eb3f8e10001e2c7d06b7ff8dff9bec66",
"components": "7aa04b6cb24b0dd8d580503f82e2b7fc5f9962f2e94bea402f4252ff1f02fb77",
"frameworkBindings": "3d6893c966874d773c2c3ebcfbedbb481b4947ec86f986aa30855ccda7ef970c"
},
"components": {
"app-button": "5f8b7e9c5cc1047b4412b73f2c2f02f1f4af5de5a5db92057f8bf21b167caa10",
"array-props": "915f61261be849baf83acf4d99b2f2c8fd1ce773aeb4532a8b1499534a57fab0",
"data-card": "7e50dfc649665d79778377450c54f1f101d7a868ef06549e7400b982c22251b0",
"form-field": "0e6d1a7d82a2ac74c612a2f8461cf3d363a6cc1135898285e5c1278f02cd995c",
"status-badge": "60cb70dd7d6bfbb788cd52bc3599e2fc4d2dfb55a889100fa35c78807f2c92bf"
},
"doNotRediscover": [],
"awaitingAuthorship": [
"categories",
"intents",
Expand Down
31 changes: 28 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aestheticfunction/dspack-export",
"version": "0.4.0",
"version": "0.5.0",
"description": "Bootstrap a current-spec dspack design-system snapshot from a component codebase (React + Tailwind/shadcn, Vue 3 + Vuetify 3) through a framework-adapter layer",
"keywords": [
"dspack",
Expand Down Expand Up @@ -58,6 +58,7 @@
"@types/react": "^18.3.20",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dspack-export-shipped": "npm:@aestheticfunction/dspack-export@^0.4.0",
"react": "^18.3.1",
"tailwind-merge": "^2.6.0",
"vitest": "^3.0.0",
Expand Down
Loading
Loading