feat: profile-as-data — loadProfile, profile.v1 schema, scaffoldProfile, CLI --profile + bin - #24
Merged
Merged
Conversation
…le, CLI --profile + bin
The Profile has always been pure data (profiles.ts); this makes it reachable
as data, which is the seam a visual catalog composer edits:
- src/transform/profile.v1.schema.json: draft-2020-12 schema mirroring the
Profile interface 1:1 plus a profileVersion:"1" envelope const.
- loadProfile(json): ajv-validated (same toolchain as the catalog gates),
typed ProfileLoadError with pathed issues; strips the envelope. A JSON
profile drives the engine byte-identically to the TS profile (tested).
- scaffoldProfile(doc, {catalogIdBase}): mechanical 1:1 draft seeded from the
json-render CatalogModel — verbatim prop projections, subFlatten derived
from declared acceptsChildren, no valueMaps, no casualties; every judgment
call returned as notes (the awaitingAuthorship analog for profiles).
- CLI: --profile <path> (exit 2 on schema refusal), bin "dspack-emit"
(shebang now plain node), sample-surface default skipped under --profile
so external contracts emit from any CI.
- Casualty refusal now cites the authored reason: a surface using a declared
casualtyComponents id fails with the profile's class + reason instead of a
bare unknown-component error (fail-first output in the PR).
All 71 tests pass incl. 9 new (round-trip byte-identity, schema negative
controls, scaffold coverage on both canonical contracts, casualty citation);
test:pack OK with the new bin + shipped schema.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n + profile smoke - version 0.4.0 (additive API: loadProfile/profileSchema/ProfileLoadError/ scaffoldProfile, CLI --profile + bin, casualty-reason refusals). - New test: unknown keys refuse at document and plan level; x-* is the one sanctioned extension surface (dspack convention), preserved by loadProfile and documented in PROFILES.md — explicit, not silent. - pack-test now also proves, from the installed tarball: JSON-profile byte-identity with the TS profile, loadProfile fail-closed, and the installed dspack-emit bin emitting with --profile and refusing an invalid profile at exit 2. 72/72 tests; pack-and-install + profile + bin smokes OK. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR makes the A2UI mapping Profile consumable “as data” by introducing a versioned JSON profile document format (schema + loader), adding a mechanical scaffold generator from a dspack contract, and wiring both into the CLI (including publishing a dspack-emit bin). It also improves surface emission errors by citing authored casualty reasons.
Changes:
- Add v1 JSON schema for
ProfileplusloadProfile()/ProfileLoadErrorfor fail-closed runtime validation. - Add
scaffoldProfile()to generate a valid v1 profile document + judgment notes from a contract’s json-render model. - Extend the CLI with
--profile <path>and package adspack-emitbin; improve casualty refusal messages in the surface emitter; add tests and docs.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| src/transform/scaffold.ts | Adds scaffoldProfile() to mechanically derive a v1 profile document + notes from a contract. |
| src/transform/profile.v1.schema.json | Introduces draft-2020-12 schema for profile v1 with fail-closed validation and x-* extension slots. |
| src/transform/profile-load.ts | Adds loadProfile() + ProfileLoadError using Ajv 2020-12 and exports the shipped schema. |
| src/targets/a2ui/surface.ts | Enhances missing-component errors to cite declared casualty class + reason when applicable. |
| src/profile-load.test.ts | Adds tests for schema validation, byte-identity behavior, scaffold validity, and casualty refusal messaging. |
| src/index.ts | Exposes loadProfile, profileSchema, and scaffoldProfile from the package entrypoint. |
| src/cli.ts | Adds --profile support, skips repo-relative default surface under --profile, and threads profile into transform/emit paths. |
| scripts/pack-test.sh | Extends packed-install smoke tests to cover JSON profiles and the installed dspack-emit bin behavior. |
| README.md | Documents JSON profiles, --profile, loadProfile, scaffoldProfile, and CLI semantics. |
| package.json | Bumps version to 0.4.0, copies schema into dist on build, and adds the dspack-emit bin entry. |
| docs/PROFILES.md | Documents profile-as-data format, schema behavior, and how to use JSON profiles and scaffolding. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+159
to
+169
| if (args.profile) { | ||
| try { | ||
| profile = loadProfile(JSON.parse(readFileSync(resolve(args.profile), "utf8"))); | ||
| } catch (e) { | ||
| if (e instanceof ProfileLoadError) { | ||
| console.error(`error: --profile ${args.profile}: ${e.message}`); | ||
| process.exit(2); | ||
| } | ||
| throw e; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Makes the a2ui mapping
Profilereachable as data — the seam a visual catalog composer edits. First PR of a three-repo release train (this → dspack-gen#48 → dspack-studio#27).src/transform/profile.v1.schema.json— draft-2020-12 schema mirroring theProfileinterface 1:1, plus aprofileVersion: "1"envelope const. Shipped in the package (dist/transform/). One deliberate, documented extension beyond the TS type:x-*keys are accepted and preserved (document/plan/casualty levels) — the dspack convention, so authoring tools can carry provenance without schema changes; the engine never reads them. Everything else fails closed (additionalProperties: false, tested at document and plan level).loadProfile(json)— ajv 2020 /strict:false/ ajv-formats (the exact toolchain of the catalog gates); throws typedProfileLoadErrorwith pathed issues; strips the envelope. A JSON profile drives the engine byte-identically to the TS profile (tested in-repo AND from the packed tarball).scaffoldProfile(doc, { catalogIdBase })— mechanical 1:1 draft seeded from the json-renderCatalogModel: verbatim prop projections,subFlattenderived from declaredacceptsChildren, no valueMaps, no casualties. Every judgment call returned asnotes(the profile analog of dspack-export'sawaitingAuthorship). No governance invented.--profile <path>(schema refusal → exit 2), packagebindspack-emit(shebang now plainnode), repo-relative sample-surface default skipped under--profileso out-of-repo contracts emit from any CI.casualtyComponentsid fails with the profile's class + reason instead of a bare unknown-component error.Verification
shadcnProfile; schema negative controls (kind:"vibes", non-httpscatalogIdBase, missing envelope, unknown keys at document + plan level with thex-*positive control); scaffold validity + full coverage + verbatim-enum seeding on both canonical contracts; casualty citation (landed fail-first — output below).npm run test:packproves from the installed tarball: exports map intact, A1–A3 pass, typed errors, JSON-profile byte-identity,loadProfilefail-closed, and the installeddspack-emitbin emitting with--profile+ refusing an invalid profile at exit 2.kind:"string"/"boolean"propMaps (previously untested by in-repo profiles).Fail-first evidence (casualty citation, run against pre-change
surface.ts):Scope boundaries
No engine/mapping behavior changes; no changes to existing emitted artifacts (goldens untouched,
out/regenerates identically for the shadcn profile); json-render target untouched; no new runtime dependencies.Risks & rollback
binshebang change (npx tsx→node) only affects the new bin path; in-reponpm run transformstill uses tsx.Release
Version: 0.4.0 (bumped in this PR). Publish is manual and stays with you:
Suggested release notes:
Downstream: dspack-gen#48 widens its emit range to admit 0.4.x; dspack-studio#27 replaces its temporary override with
^0.4.0once this is on npm.🤖 Generated with Claude Code