From fd8dbadf7b4a1fa0fac4773d9226f0c19b8b66ba Mon Sep 17 00:00:00 2001 From: Noah Schatz Date: Fri, 25 Sep 2026 21:02:03 +0000 Subject: [PATCH] Scope the seed-to-bytes promise to the installed parser versions The 0.1.0 changeset and three documentation pages promised that a golden fixture pinned to a synth version keeps matching. Measured on this tree: with the vendored @cosyte/ccda 0.0.1 swapped for the 0.0.15 on npm, the same C-CDA seeds serialize to different bytes (the determinism digest fails for ccda seeds 1001, 4242, 60501 and 813377, and the three C-CDA golden fixtures and three C-CDA quirk fixtures stop matching), while the HL7 v2, FHIR, X12, NCPDP and ASTM pairs stay byte-identical. Each format's bytes come from that parser's own builder, so pinning synth alone does not pin them. The changeset and the limitations, overview and troubleshooting pages now tell a reader to pin the parsers they generate with alongside synth and the seed. Wording only: no code, test or gate changes. Claude-Session: https://claude.ai/code/session_01U24bnHKNxQWMxmfN7ny7w9 --- .changeset/what-0-1-means.md | 2 +- docs-content/guides-overview.md | 3 ++- docs-content/limitations.md | 5 ++++- docs-content/troubleshooting.md | 3 ++- 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.changeset/what-0-1-means.md b/.changeset/what-0-1-means.md index b7f3876..c5b514e 100644 --- a/.changeset/what-0-1-means.md +++ b/.changeset/what-0-1-means.md @@ -6,7 +6,7 @@ **What is covered, and what you can depend on.** Seeded generation of spec-clean test fixtures in six formats, each built through that format's own `@cosyte/*` builder or serializer and read back by its parser with zero warnings: HL7 v2 (ADT, ORU, ORM, SIU and VXU), FHIR R4 and US Core (the clinical resources, fixtures requested by US Core 6.1.0 profile name, and `collection`, `transaction` and `document` Bundles), C-CDA R2.1 (CCD and Referral Note), X12 005010 (837P, 837I, 837D, 835 and 271), NCPDP SCRIPT (NewRx, RxRenewalRequest and RxChangeRequest) and Telecom (B1, B2 and B3), and ASTM E1394 records with E1381 framing. Every identifier, name, date, phone and address comes from a reserved range or the shipped fake-name pool, never from real data. Also covered: vendor-quirk fixtures for HL7 v2, C-CDA and ASTM, each round-tripping to exactly one intended warning; the pairing loop with `@cosyte/deid`; the `safe` value providers and their validators; and the stable `SYNTH_*` fatal codes. Node.js 22 and 24, ESM and CommonJS, with type declarations for both, and no third-party runtime dependency. -**What the version promises.** The same seed yields byte-identical output, verified across Node 22 and Node 24 on every change, and a change to what a seed maps to ships only in a release that declares a major version, so a golden fixture pinned to a `0.x` version keeps matching. For the rest of the API: until 1.0, a breaking change raises the minor version (0.1 to 0.2), and the changelog entry says what broke and what to change. A patch release (0.1.x) does not break you, so a `^0.1.0` range takes the patches and stops before 0.2.0. +**What the version promises.** The same seed yields byte-identical output, verified across Node 22 and Node 24 on every change. Each format's bytes come from that format's own `@cosyte/*` builder, so the mapping holds for a given set of installed parsers: for a long-lived golden fixture, pin `@cosyte/synth` and the parsers you generate with alongside the seed. With the parsers unchanged, a change to what a seed maps to ships only in a release that declares a major version. For the rest of the API: until 1.0, a breaking change raises the minor version (0.1 to 0.2), and the changelog entry says what broke and what to change. A patch release (0.1.x) does not break you, so a `^0.1.0` range takes the patches and stops before 0.2.0. **What is not covered yet.** Quirk recipes for FHIR, X12 and NCPDP; the X12 270 eligibility request and the NCPDP SCRIPT lifecycle responses, which wait on builders in the parsers; `@cosyte/deid` pairing for NCPDP SCRIPT, ASTM and DICOM; DICOM generation; and clinical realism, since this is a format and conformance generator rather than a clinical simulator. The pairing loop co-validates the two packages on this package's own output and is not an independent audit of `@cosyte/deid`. The documentation at https://docs.cosyte.com/synth lists the known limitations in full. diff --git a/docs-content/guides-overview.md b/docs-content/guides-overview.md index 006c344..efbf2f0 100644 --- a/docs-content/guides-overview.md +++ b/docs-content/guides-overview.md @@ -23,7 +23,8 @@ to one real fixture-generation question. ## Pin a seed for a reusable golden fixture A `Corpus` is a seed plus a manifest of what was generated: deep-frozen and self-describing. Pin the -seed (and the `synth` version) and every downstream run regenerates the identical set: +seed (and the versions of `synth` and the parsers you generate with) and every downstream run +regenerates the identical set: ```ts runnable import { hl7Corpus } from "@cosyte/synth/hl7"; diff --git a/docs-content/limitations.md b/docs-content/limitations.md index f1b12b6..e36428d 100644 --- a/docs-content/limitations.md +++ b/docs-content/limitations.md @@ -177,7 +177,10 @@ ssnOk && isSyntheticNpi(safe.npi(rng)) && isSyntheticDea(safe.dea(rng)); // => t A seed maps to the same bytes **within a documented compatibility window**, not across _major_ `synth` versions. A version bump may change a value list or the algorithm and thus the seed→bytes mapping; that is a **documented breaking change**. For a long-lived golden fixture, **pin the `synth` -version alongside the seed.** +version, and the versions of the parsers you generate with, alongside the seed.** Each format's bytes +come from that parser's own builder and serializer, so upgrading a parser can change them even when +`synth` has not moved: measured, the same C-CDA seeds serialize differently under two releases of +`@cosyte/ccda`. **Which engines that is verified across, and how.** The seed→bytes mapping is verified **byte-identical across Node 22 and Node 24**, on every change. It is verified by **comparing diff --git a/docs-content/troubleshooting.md b/docs-content/troubleshooting.md index e705903..5171942 100644 --- a/docs-content/troubleshooting.md +++ b/docs-content/troubleshooting.md @@ -21,7 +21,8 @@ generateAdt({ seed: 5 }).toString() === generateAdt({ seed: 5 }).toString(); // ``` Note that a **`synth` version bump may change the seed→bytes mapping**: that is a documented breaking -change, so pin the version alongside the seed for a long-lived golden fixture. +change, so pin the version alongside the seed for a long-lived golden fixture. Pin the parser peers +too: each format's bytes come from that parser's own builder, so a parser upgrade can change them. ## "Cannot find module @cosyte/hl7"