feat(cli): freeze and version-stamp the --json contract (#19)#35
Merged
Conversation
dv release runs after dv version consumed the Records, so it can't re-render the notes. But dv owns the CHANGELOG format, so a first-party extractor recovers them from the file dv itself wrote — instead of every consumer rolling a fragile slice (the .github release script did exactly that). - New pure changelog/extract.ts (extractReleaseSection): the inverse of renderReleaseSection — slices the `## [version]` body back out. - New required `releaseNotes` field on Plan.awaitingRelease entries (plan-schema.ts + regenerated specs/schemas/plan.json). Empty string when no section is found; never absent. - release.ts populates it at the command edge (CHANGELOG IO stays out of the pure plan builder), so status/version Plans keep `""`. - DRY: the duplicated resolveOutputPathFromTemplate in version.ts and v1.ts moves to the changelog subtool (changelog/path.ts), now shared by version, v1, and release. - .github/scripts/release.ts drops its private readChangelogSection / findChangelogPath and reads entry.releaseNotes directly. Tests: extract round-trip / EOF / missing-version; a release integration test asserting notes are populated from the CHANGELOG (and "" when none). deno task verify passes; 380 tests. Closes #18. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every dv --json output is now a frozen, versioned contract: it carries a
schema URN and validates against a committed JSON Schema generated from a
Zod source.
- New central registry domain/schema-urns.ts (SCHEMA_URNS) — the single
source of truth for every contract id. All the scattered hardcoded URN
string literals (status/v1 Plan, validate, rename, migrate, plugin-*,
init) now reference it.
- dv release --json gained its previously-missing envelope schema stamp
(RunReleaseResult.schema); the no-op / dry-run / real paths now share
one built result object.
- New Zod sources for all eight --json result envelopes
(cli/schemas/json-contracts.ts), registered in the generator so
specs/schemas/{validation-report,release-result,rename-result,
migrate-config-result,init-result,plugin-{list,verify,invoke}-result}.json
are committed and covered by the schemas:check drift gate.
- Freeze gate: assertVersionedSchemaUrns() runs in the generator and
rejects any contract id missing the urn:dv:schema:vN: version prefix.
Tests: freeze-gate test; URN ↔ Zod ↔ committed-file correspondence for
every envelope; release --json output validated against the release-result
schema. Verified real validate / plugin-list output conforms. 398 tests,
schemas in sync, deno task verify green.
Closes #19.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
e3d0b98 to
34c68e7
Compare
The new public export SCHEMA_URNS had an inferred (`as const`) type, which `deno publish` rejects under the slow-types gate (missing-explicit-type). Give it an explicit `SchemaUrns` interface, and make `urn()` return a template-literal type so each member keeps its precise literal URN type — consumers rely on those literals (`z.literal(SCHEMA_URNS.plan)`, `schema: typeof SCHEMA_URNS.releaseResult`). deno task publish:check now passes; full verify green (398 tests). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…19) The slow-types fix made public result types (RunReleaseResult, Plan, ValidationReport) reference `typeof SCHEMA_URNS` — a value — which `deno doc --lint` rejects as a private-type-ref. Reference the public `SchemaUrns` interface instead (`schema: SchemaUrns["releaseResult"]`), export the interface + URN types from lib.ts so they're part of the public API, and JSDoc each interface member (doc-lint requires it). All CI gates pass locally: fmt, lint, check, test (398), schemas:check, publish:check, doc:lint. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #19.
Every
dv … --jsonoutput is now a frozen, versioned contract: it carries a schema URN and validates against a committed JSON Schema generated from a Zod source.What changed
domain/schema-urns.ts(SCHEMA_URNS) — the single source of truth for every contract id. All the scattered hardcoded URN string literals (status/v1 Plan, validate, rename, migrate, plugin-list/verify/invoke, init) now reference it.dv release --json(RunReleaseResult) gained its missing envelopeschemafield; the no-op / dry-run / real paths now share one built result object.cli/schemas/json-contracts.ts, registered in the generator so these get committed + drift-gated:validation-report,release-result,rename-result,migrate-config-result,init-result,plugin-list-result,plugin-verify-result,plugin-invoke-result.assertVersionedSchemaUrns()runs inside the generator (so it fails bothschemas:generateandschemas:check) and rejects any contract id missing theurn:dv:schema:vN:version prefix.specs/schemas/README.mdupdated to document the envelope contracts and the generation/registry discipline.Faithfulness note
The schemas mirror exactly what each command emits (nullable fields where the command writes
?? null, thedryRunfield rename emits, etc.) — the committed schema, not the TS interface, is the frozen contract. The tests keep them aligned.Tests
schema-urns.test.ts).json-contracts.test.ts).dv release --jsonoutput validated againstrelease-resultschema across no-op / dry-run / real paths.dv validate/dv plugin listoutput parses cleanly against the Zod schemas.398 tests pass;
schemas:checkreports 13 files in sync;deno task verifygreen. A Record (feat→@dv-cli/dv) is included.🤖 Generated with Claude Code