Skip to content

feat: DX-3 bootstrap boundary — v0.4 emission, x-bootstrap ledger, refusal table (0.3.0)#9

Merged
ryandmonk merged 2 commits into
mainfrom
feat/dx3-bootstrap-boundary
Jul 22, 2026
Merged

feat: DX-3 bootstrap boundary — v0.4 emission, x-bootstrap ledger, refusal table (0.3.0)#9
ryandmonk merged 2 commits into
mainfrom
feat/dx3-bootstrap-boundary

Conversation

@ryandmonk

Copy link
Copy Markdown
Contributor

Implements the accepted DX-3 bootstrap design (§2 boundary, §3 regeneration, §5 transition).

What changes

  • Current-spec emission: snapshots declare dspack: "0.4" (single SPEC_VERSION constant); the adoption journey's manual relabel step disappears. Populated sections unchanged — the tool still never writes governance content.
  • The x-bootstrap ledger (non-semantic, per the approved design): spec version, content hash per generated section, awaitingAuthorship list. No empty governance blocks. Deleting the ledger = graduation to human-owned.
  • Refusal table: generate refuses on no-ledger / human-authored sections present / edited generated sections — each refusal names what it found and points at the ADOPTING.md workflow and --out. No force flag. Pure untouched snapshots still regenerate in place.
  • --out <file> on generate.
  • validate now gates against a vendored byte copy of the dspack v0.4 schema (shape only; contract semantics stay upstream), guarded by scripts/check-sync.mjs.
  • Publishable: private removed, files field, version 0.3.0, CHANGELOG; CI (test.yml) + tag-triggered OIDC release.yml mirroring ds-mcp/dspack-gen.

Invariants held (from the design approval)

  • Regeneration never destroys human-authored content — enforced by the table, exercised end to end via the real CLI (enriched → refused naming intents; graduated/no-ledger → refused; --out fresh path → succeeds).
  • Every refusal explains why.
  • One validator: the vendored schema is a byte-synced shape gate on this tool's own output; contract validation semantics remain solely in the dspack harness.

Fail-first evidence

src/tests/bootstrap.test.ts on pre-boundary code: Failed to load url ../emit/bootstrap.js … Test Files 1 failed (1). After implementation: 13/13. Full suite 106/106 including the ds-mcp round-trip gate. The four golden fixtures were regenerated deliberately; their diffs are exactly the header + ledger (content sections byte-identical).

After merge

Tag v0.3.0 → OIDC publish (requires one-time trusted-publisher registration on npmjs.com: repository aestheticfunction/dspack-export, workflow release.yml). Per the owner decision, publication ships only together with this change. The companion dspack PR updates ADOPTING.md (step 1 guarantee wording, step 2 removal) in lockstep.

🤖 Generated with Claude Code

…fusal table (0.3.0)

Implements dspack rfc/dx3-bootstrap-design.md:

- Snapshots declare the current spec version (0.4) via SPEC_VERSION in
  the new src/emit/bootstrap.ts; the adoption journey's manual relabel
  step disappears. Populated sections unchanged — no governance content
  is ever emitted.
- Every snapshot carries the non-semantic metadata["x-bootstrap"] ledger:
  spec version, content hash per generated section, awaitingAuthorship
  list. Deleting the ledger graduates the document to human-owned.
- Regeneration refusal table: generate refuses on no-ledger,
  human-authored sections, or edited generated sections — naming what it
  found — and never destroys human-authored content. No force flag.
  --out writes to an explicit path.
- validate checks against a vendored byte copy of the dspack v0.4 schema
  (shape gate only), guarded by scripts/check-sync.mjs.
- CI test.yml and tag-triggered OIDC release.yml (ds-mcp pattern);
  package is publishable (private flag removed, files field, 0.3.0).

Fail-first: src/tests/bootstrap.test.ts failed on the pre-boundary code
(module absent, "Test Files 1 failed"); 13/13 after implementation.
Full suite 106/106 including the ds-mcp round-trip; the four golden
fixtures regenerate with exactly the header/ledger diff and untouched
content sections. Refusal table exercised end to end via the real CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements the DX-3 bootstrap boundary for dspack-export, moving snapshot emission to spec v0.4, adding a non-semantic metadata["x-bootstrap"] ledger to track generated-section ownership, and enforcing a refusal table to prevent overwriting human-authored/enriched documents. This also prepares the project for an npm-published v0.3.0 release with CI and release automation.

Changes:

  • Emit dspack: "0.4" via a single SPEC_VERSION, and attach an x-bootstrap ledger (hashes for generated sections + awaiting-authorship checklist).
  • Add regeneration refusal logic (no ledger / human-authored sections present / generated sections edited) and support generate --out <file>.
  • Update validation to gate against a vendored v0.4 schema copy with a drift-check script, plus CI/release workflows and updated fixtures/docs for v0.3.0 publishing.

Reviewed changes

Copilot reviewed 17 out of 18 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/tests/bootstrap.test.ts New DX-3 boundary tests covering v0.4 emission, ledger behavior, and refusal table.
src/generate.ts Bumps embedded generator version to 0.3.0.
src/emit/validate.ts Switches schema validation from v0.2 to vendored v0.4 schema.
src/emit/schema/dspack.v0.4.schema.json Adds vendored dspack v0.4 JSON Schema used as a shape gate.
src/emit/bootstrap.ts Introduces SPEC_VERSION, ledger hashing, and regeneration refusal decision logic.
src/emit/assemble.ts Emits current spec version and writes metadata["x-bootstrap"] ledger during assembly.
src/cli.ts Adds refusal enforcement before writing, adds --out, updates messaging to v0.4/shape-only validation.
scripts/check-sync.mjs Adds byte-for-byte drift check against upstream dspack schema (with --write resync).
README.md Updates positioning/docs for v0.4 bootstrap boundary and npm installation.
package.json Prepares npm publish (removes private, sets files, adds check:sync, bumps to 0.3.0).
fixtures/vuetify-demo/vuetify-demo.dspack.json Regenerates fixture header + ledger for v0.4/bootstrap.
fixtures/shadcn-v4-demo/shadcn-v4-demo.dspack.json Regenerates fixture header + ledger for v0.4/bootstrap.
fixtures/shadcn-demo/shadcn-demo.dspack.json Regenerates fixture header + ledger for v0.4/bootstrap.
fixtures/dtcg-tokens-demo/dtcg-tokens-demo.dspack.json Regenerates fixture header + ledger for v0.4/bootstrap.
CHANGELOG.md Adds changelog entries for 0.3.0 and 0.2.0-alpha.0.
.gitignore Adds .DS_Store.
.github/workflows/test.yml Adds CI workflow running build/test + schema drift check.
.github/workflows/release.yml Adds tag-triggered OIDC trusted-publisher npm release workflow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/emit/bootstrap.ts
Comment on lines +94 to +118
const metadata = (existing.metadata ?? {}) as Record<string, unknown>;
const ledger = metadata['x-bootstrap'] as BootstrapLedger | undefined;
if (!ledger || typeof ledger !== 'object' || typeof ledger.generated !== 'object') {
return {
allow: false,
reason: `the existing document carries no bootstrap ledger (metadata["x-bootstrap"]), so it is human-owned and this tool will not touch it. ${WORKFLOW_HINT}`,
};
}

const humanSections = Object.keys(existing).filter(
(key) => !BASE_KEYS.has(key) && !(key in ledger.generated),
);
if (humanSections.length > 0) {
return {
allow: false,
reason: `the existing document contains human-authored sections (${humanSections.join(', ')}); regenerating would sit alongside content this tool does not own. ${WORKFLOW_HINT}`,
};
}

const edited = Object.entries(ledger.generated)
.filter(([section, recorded]) => {
const value = existing[section];
return value === undefined || sectionHash(value) !== recorded;
})
.map(([section]) => section);
* generator.
*/
import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
… node 20

The repo claimed engines >=20 but was never CI-tested; the first CI run
caught it. Declare the real floor (engines >=22, CI node 22).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ryandmonk
ryandmonk merged commit 3da897c into main Jul 22, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants