Skip to content

feat: importable validation harness (lib/validate.mjs) - #34

Merged
ryandmonk merged 2 commits into
mainfrom
feat/importable-harness
Aug 3, 2026
Merged

feat: importable validation harness (lib/validate.mjs)#34
ryandmonk merged 2 commits into
mainfrom
feat/importable-harness

Conversation

@ryandmonk

Copy link
Copy Markdown
Contributor

What

Extracts the validation harness into an importable, pure library — the repo's own RFC calls the current state a violation of the one-validator principle ("the published CLI is a distribution of the harness, never a fork", rfc/dx3-bootstrap-design §4). The consuming feature is dspack-studio's Phase 2 composer: in-browser document validation with the same checks and wording as the CLI.

  • lib/validate.mjs — every check moved verbatim (S2 checkVocabulary, checkGovernance with S1 over examples[], checkCategories, validateDocument, buildVocabulary); compileSchemaSet takes schemas injected by the caller (no fs — Node reads files, a browser bundler imports the JSON); plus stripAdditiveBlocks (the back-compat strip, previously inline in main) and documentReport ({valid, version, errors} for hosts). Imports: ajv + ajv-formats only.
  • scripts/validate.mjs — now the filesystem/process front-end over the lib. All three modes (default, --fixtures negative, --file) unchanged; CLI output byte-identical to the pre-extraction harness (diffed; the only delta is npm's version banner).
  • scripts/check-lib-boundary.mjs (CI-wired as check:lib) — purity gate (ajv-only imports) + non-vacuity through the import surface: 2 examples accepted incl. back-compat strip, 18 negative fixtures rejected, via the lib directly. If extraction ever drifts a check, this gate and the CLI disagree loudly.
  • lib/validate.d.ts hand-written types; lib/ in files; README "Validating dspack files programmatically" section; version 0.4.2.

Deliberate choices

  • No exports map. Sibling repos deep-import schema/*.json and examples/*.dspack.json by path today (af-site's gates, ds-mcp's sync checks); an exports map would break them. The lib is imported by file path: @aestheticfunction/dspack-spec/lib/validate.mjs.
  • Error strings, not structured findings. validateDocument returns the exact strings the CLI prints (paths embedded) — zero semantic drift, wording shared by construction. A structured-findings shape can layer on later without touching the checks.
  • Scope split unchanged: this harness validates documents (schema, consistency, S1/S2 over the contract's own examples). S3 rule evaluation stays in dspack-gen — not moved, not duplicated.

Verification

  • npm run validate (2 examples PASS), --fixtures negative (18/18 rejected), --file both directions — all green, output byte-identical vs old harness.
  • npm run check:lib PASS (purity + full corpus through the import).
  • CI gains the check:lib step between negative fixtures and file mode.

Release

Version 0.4.2 in-branch (package-level additive change; the spec itself is untouched, so no 0.5 signal). Publishes via the existing tag-triggered OIDC workflow (git tag v0.4.2) after merge.

🤖 Generated with Claude Code

ryandmonk and others added 2 commits August 3, 2026 17:30
The harness's checks were already pure functions trapped in a CLI script;
this makes the one-validator principle (rfc/dx3-bootstrap-design §4)
importable instead of aspirational:

- lib/validate.mjs: every check moved verbatim — buildVocabulary,
  checkVocabulary (S2), checkGovernance (incl. S1 over examples),
  checkCategories, validateDocument — plus compileSchemaSet (schemas
  INJECTED by the caller: no filesystem, no process control, ajv-only
  imports, so the identical code runs in a browser bundle) and two small
  additions: stripAdditiveBlocks (the back-compat strip, previously inline
  in main) and documentReport ({valid, version, errors} for hosts).
- scripts/validate.mjs: now the filesystem-and-process front-end over the
  lib — a distribution of the harness, never a fork. CLI output is
  byte-identical to the pre-extraction harness (diffed; only npm's version
  banner changes).
- scripts/check-lib-boundary.mjs (CI-wired as check:lib): purity gate
  (ajv-only imports) + non-vacuity through the IMPORT surface — all
  examples accepted (with back-compat strip), all 18 negative fixtures
  rejected, via the lib directly.
- lib/validate.d.ts hand-written types; lib/ added to files; README
  programmatic-validation section; version 0.4.2.

No exports map, deliberately: every published file stays deep-importable
(af-site and siblings resolve schema/ and examples/ by path today).

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

Copy link
Copy Markdown
Contributor Author

Final release verification (train pass)

  • CLI byte-identity re-proven during development (diff vs pre-extraction harness: only npm's version banner differs).
  • check:lib green: purity (ajv-only imports) + the full corpus (2 examples accepted incl. back-compat strip, 18 negatives rejected) through the import surface.
  • Packed-tarball verification: lib/validate.mjs imports from the installed package, compileSchemaSet + documentReport accept the shipped astryx example and refuse an unknown version, lib/validate.d.mts ships, and the installed dspack-validate bin passes file mode. No exports map (deliberate — deep imports preserved for af-site and siblings).

0.4.2 release notes:

0.4.2 — the harness is importable. lib/validate.mjs: every check behind dspack-validate as pure functions (schemas injected; ajv-only imports; browser-safe), plus stripAdditiveBlocks and documentReport. The CLI is now a front-end over this one implementation — output byte-identical. New CI gate check:lib replays the example + negative-fixture corpus through the import surface. Types in lib/validate.d.mts. The spec itself is unchanged.

Publish after merge: git tag v0.4.2 && git push origin v0.4.2 (OIDC).

@ryandmonk
ryandmonk marked this pull request as ready for review August 3, 2026 22:47
Copilot AI review requested due to automatic review settings August 3, 2026 22:47
@ryandmonk
ryandmonk merged commit 98d235c into main Aug 3, 2026
2 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Not ready to approve

The new library can throw at runtime if the surface schema isn’t injected, and the README example uses a JSON import syntax inconsistent with the repo’s declared Node >=20 support.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.

Pull request overview

This PR extracts the dspack validation harness into an importable, pure ESM library (lib/validate.mjs) so programmatic consumers (including browser bundles) can reuse the exact same validation checks and error strings as the CLI, while keeping the CLI as a filesystem/process front-end.

Changes:

  • Added lib/validate.mjs (+ lib/validate.d.mts) exporting schema compilation and document validation utilities.
  • Refactored scripts/validate.mjs to delegate all validation logic to the library.
  • Added a CI gate (scripts/check-lib-boundary.mjs, wired via npm run check:lib and the workflow) to enforce library purity and corpus validation through the import surface.
File summaries
File Description
scripts/validate.mjs Reworked CLI to use lib/validate.mjs for all checks and back-compat stripping.
scripts/check-lib-boundary.mjs New CI gate: verifies library import purity and runs the corpus through the library API.
README.md Documents programmatic validation via the new importable harness.
package.json Bumps version to 0.4.2, publishes lib/, and adds check:lib script.
lib/validate.mjs New pure validation library (Ajv-only imports; injected schemas).
lib/validate.d.mts New TypeScript declarations for the library API.
.github/workflows/validate.yml Adds npm run check:lib to CI.
Review details
  • Files reviewed: 7/7 changed files
  • Comments generated: 2
  • Review effort level: Lite

We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.

Comment thread lib/validate.mjs
Comment on lines +314 to +316
if (GOVERNANCE_VERSIONS.has(version)) {
errors.push(...checkGovernance(doc, validators.get(SURFACE_SCHEMA)));
}
Comment thread README.md
Comment on lines +131 to +132
import v04 from "@aestheticfunction/dspack-spec/schema/dspack.v0.4.schema.json" with { type: "json" };
import surface from "@aestheticfunction/dspack-spec/schema/dspack.surface.v0_1.schema.json" with { type: "json" };
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