Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,21 @@
# @cosyte/synth

> Deterministic, seedable **synthetic healthcare-fixture generator** for Node.js and TypeScript:
> spec-clean by construction, and **never real PHI**.
> built through each parser's own builder, and **never real PHI**.

`@cosyte/synth` generates reproducible synthetic test corpora across the six Cosyte formats (HL7 v2,
FHIR R4 / US Core, C-CDA, X12, NCPDP, and ASTM). It is a **consumer** of the
cosyte parsers, not a parser: it builds each artifact **through the parser's own builder/serializer**
(so the output is spec-clean by the same mechanism the parser proves) and draws every identifier, name,
(so each artifact re-parses through that parser with zero warnings) and draws every identifier, name,
date, phone, and address from a **guaranteed-non-colliding synthetic source**. It is a
**format/conformance generator, not a clinical simulator**: it does not model disease progression
(that is Synthea).

> **Status:** `0.1`, published to npm. The version shown on the npm package page is the one that is
> live; this page never repeats it. Below 1.0, a breaking change raises the minor version and the
> changelog says what broke. The generator is **feature-complete**: the
> seeded-PRNG core, the synthetic-safety providers, and the round-trip harness; **spec-clean generation
> across all six formats**, HL7 v2 (`ADT`/`ORU`/`ORM`/`SIU`/`VXU`), FHIR R4 / US Core (the full clinical
> seeded-PRNG core, the synthetic-safety providers, and the round-trip harness; **generation across all
> six formats**, HL7 v2 (`ADT`/`ORU`/`ORM`/`SIU`/`VXU`), FHIR R4 / US Core (the full clinical
> set + `collection`/`transaction`/`document` Bundles), C-CDA R2.1 (CCD + Referral Note), X12 005010
> (837P/I/D, 835, 271), NCPDP (SCRIPT NewRx / RxRenewal / RxChange + Telecom B1/B2/B3), and ASTM (E1394
> record reports + E1381 framing), each built **through its parser's own builder/serializer**; **vendor-quirk
Expand Down Expand Up @@ -136,7 +136,7 @@ corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean

`@cosyte/ccda` is an **optional peer dependency**, needed only for the `@cosyte/synth/ccda` subpath.

## Generate a spec-clean X12 transaction
## Generate an X12 transaction

The `@cosyte/synth/x12` subpath builds HIPAA **005010** transactions **through `@cosyte/x12`'s domain
builders** (`build837P/I/D`, `build835`, `build271`), so the ISA/GS/ST…SE/GE/IEA envelope, the
Expand All @@ -159,15 +159,15 @@ roundTrip(generate271({ seed: 3 })).specClean; // true

// Or a reproducible mixed corpus (837P/I/D + 835 + 271):
const corpus = x12Corpus({ seed: 42 });
corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
corpus.artifacts.every((a) => a.warnings.length === 0); // true
```

`@cosyte/x12` is an **optional peer dependency**, needed only for the `@cosyte/synth/x12` subpath.

**Deferred:** the **270** eligibility _request_ (`@cosyte/x12` ships a `build271` but no `build270`, and
`synth` never hand-writes bytes around a missing builder) and **vendor-quirk mode**.

## Generate a spec-clean NCPDP message
## Generate an NCPDP message

The `@cosyte/synth/ncpdp` subpath builds both NCPDP standards **through `@cosyte/ncpdp`'s own emit
surface**, so each message round-trips through the parser with **zero warnings**. It emits **SCRIPT**
Expand All @@ -193,15 +193,15 @@ telecomRoundTrip(generateB1({ seed: 777 })).specClean; // true

// Or a reproducible mixed corpus (NewRx + RxRenewal + RxChange + B1 + B2 + B3):
const corpus = ncpdpCorpus({ seed: 42 });
corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
corpus.artifacts.every((a) => a.warnings.length === 0); // true
```

`@cosyte/ncpdp` is an **optional peer dependency**, needed only for the `@cosyte/synth/ncpdp` subpath.

**Deferred:** vendor-quirk mode. SCRIPT coverage tracks the parser's builder surface: the
renewal/change _responses_ land as `@cosyte/ncpdp` grows builders.

## Generate a spec-clean ASTM message
## Generate an ASTM message

The `@cosyte/synth/astm` subpath builds ASTM laboratory messages **through `@cosyte/astm`'s own emit
surface**: `buildAstmMessage` for the E1394 record layer, `composeAstmFrames` for the E1381 frame
Expand Down Expand Up @@ -229,7 +229,7 @@ astmFramedRoundTrip(generateAstmResultFramed({ seed: 12345 })).specClean; // tru

// Or a reproducible mixed corpus (a result report + an order):
const corpus = astmCorpus({ seed: 42 });
corpus.artifacts.every((a) => a.warnings.length === 0); // true, all spec-clean
corpus.artifacts.every((a) => a.warnings.length === 0); // true
```

`@cosyte/astm` is an **optional peer dependency**, needed only for the `@cosyte/synth/astm` subpath.
Expand Down Expand Up @@ -276,7 +276,7 @@ vendor-attributed corpus** to ground.
## Co-validate with `@cosyte/deid` (the pairing loop)

The `@cosyte/synth/deid` subpath is a deterministic **closed-loop co-validation harness** for the
`synth` ⇄ `deid` pair: it **generates** a spec-clean artifact, **plants** distinctive synthetic PHI
`synth` ⇄ `deid` pair: it **generates** an artifact, **plants** distinctive synthetic PHI
sentinels at the patient loci, **de-identifies** it through `@cosyte/deid`, and **verifies** every
sentinel is gone from the output (a surviving sentinel is a hard failure) while the clinical payload
survives (the over-scrub guard).
Expand Down Expand Up @@ -332,8 +332,8 @@ isSyntheticNpi(safe.npi(rng)); // true, always a deliberately-invalid-Luhn NPI (
phones, RFC 2606/6761 `example.*` domains, RFC 5737/3849 TEST-NET IPs, a synthetic assigning
authority for MRNs). A CI gate proves it.
**No generated value can be real or plausibly-real PHI.**
- **Spec-clean by the parser's own judgment**, built through the parser's conservative serializer, and
checked by feeding the artifact straight back in: a spec-clean artifact re-parses with zero warnings.
- **Checked by the parser's own judgment**, built through the parser's conservative serializer and
fed straight back in: each artifact re-parses with zero warnings.
- **Deterministic**, a hand-rolled seeded PRNG (`sfc32`/`splitmix32`); `Math.random` is lint-banned.
A seed, and only the seed, determines the output, byte-for-byte, anywhere.
- **Immutable**, generated artifacts and the `Corpus` result are deep-frozen.
Expand Down
7 changes: 3 additions & 4 deletions docs-content/concepts-archetype.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,10 @@ const rng = createRng(1);
[isSyntheticSsn(safe.ssn(rng)), isSyntheticPhone(safe.phone(rng))]; // => [true, true]
```

## Spec-clean, by the parser's own judgment
## Checked by the parser's own judgment

`synth` never hand-writes bytes. It builds **through the parser's own conservative serializer**, so an
artifact is spec-clean by the exact mechanism the parser already proves, and its correctness is
checkable by feeding it straight back into that parser. A spec-clean artifact re-parses with **zero
artifact is checkable by feeding it straight back into that parser: it re-parses with **zero
warnings** and re-serializes byte-identically:

```ts runnable
Expand All @@ -55,7 +54,7 @@ pipeline without defensive copying.
## Stable fatal codes

A generator has no input to tolerate, so its diagnostics are **fatal**: a request it cannot honor
spec-clean (an unsupported format or quirk) throws a typed `SynthError` carrying a stable
(an unsupported format or quirk) throws a typed `SynthError` carrying a stable
`SYNTH_FATAL_CODES` value, never a silent fabrication. Codes are `key === value`, so the full set
survives an `Object.values(...)` snapshot into a stability tripwire.

Expand Down
9 changes: 4 additions & 5 deletions docs-content/guides-astm.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ sidebar_position: 7

The `@cosyte/synth/astm` subpath builds ASTM laboratory messages **through `@cosyte/astm`'s own emit
surface**: `buildAstmMessage` for the E1394 record layer and `composeAstmFrames` for the E1381 frame
layer, so every message is **spec-clean by construction** and round-trips through the parser with
**zero warnings**. `@cosyte/astm` is an **optional peer dependency**, needed only for this subpath.
layer, so every message round-trips through the parser with **zero warnings**. `@cosyte/astm` is an **optional peer dependency**, needed only for this subpath.

The surface covers both layers:

Expand All @@ -27,10 +26,10 @@ PHI. The practice- and lab-assigned IDs are minted independently, so they stay *
`@cosyte/astm` keeps them distinct on parse). `synth` is a **format/conformance generator, not a clinical
simulator**: a generated result pairs a code and a value with no claim of clinical coherence.

## A result report, spec-clean by construction
## A result report

The round-trip harness proves spec-cleanliness by `@cosyte/astm`'s own judgment: a spec-clean message
re-parses with zero warnings and re-serializes byte-identically:
The round-trip harness checks a message by `@cosyte/astm`'s own judgment: it re-parses with zero
warnings and re-serializes byte-identically:

```ts runnable
import { generateAstmResult, astmRoundTrip } from "@cosyte/synth/astm";
Expand Down
4 changes: 2 additions & 2 deletions docs-content/guides-deid.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ A synthetic-fixture generator and a de-identifier are natural partners: `@cosyte
that **looks like** PHI but contains none, and `@cosyte/deid` removes PHI. The `@cosyte/synth/deid`
subpath closes the loop between them, a deterministic, seeded **co-validation harness**:

1. **Generate** a spec-clean synthetic artifact through `synth`'s own generators.
1. **Generate** a synthetic artifact through `synth`'s own generators.
2. **Enumerate** the distinctive synthetic PHI sentinels `synth` planted at the patient loci.
3. **De-identify** it through `@cosyte/deid`.
4. **Verify** every sentinel is gone from the de-identified output: a surviving sentinel is a hard
Expand Down Expand Up @@ -74,7 +74,7 @@ locates a locus but fails to strip it is still caught.

## Deterministic and seeded

The loop is a pure function of the seed: the same seed yields the byte-identical spec-clean artifact,
The loop is a pure function of the seed: the same seed yields the byte-identical artifact,
the byte-identical de-identified output, and the same sentinel set.

```ts runnable
Expand Down
7 changes: 3 additions & 4 deletions docs-content/guides-ncpdp.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ sidebar_position: 6
# Generate NCPDP messages

The `@cosyte/synth/ncpdp` subpath builds both NCPDP standards **through `@cosyte/ncpdp`'s own emit
surface**, so every message is **spec-clean by construction** and round-trips through the parser with
**zero warnings**. `@cosyte/ncpdp` is an **optional peer dependency**, needed only for this subpath.
surface**, so every message round-trips through the parser with **zero warnings**. `@cosyte/ncpdp` is an **optional peer dependency**, needed only for this subpath.

NCPDP is two structurally unrelated standards under one brand, and the surface covers both:

Expand All @@ -21,8 +20,8 @@ NCPDP is two structurally unrelated standards under one brand, and the surface c

## A NewRx, spec-clean by construction

The round-trip harness proves spec-cleanliness by `@cosyte/ncpdp`'s own judgment: a spec-clean message
re-parses with zero warnings and re-serializes byte-identically:
The round-trip harness checks a message by `@cosyte/ncpdp`'s own judgment: it re-parses with zero
warnings and re-serializes byte-identically:

```ts runnable
import { generateNewRx, scriptRoundTrip } from "@cosyte/synth/ncpdp";
Expand Down
6 changes: 3 additions & 3 deletions docs-content/guides-overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ to one real fixture-generation question.
> `@cosyte/ccda`'s `buildCcda`, the [C-CDA guide](./guides-ccda)); X12 005010 (837P/I/D, 835, 271 via
> `@cosyte/x12`, the [X12 guide](./guides-x12)); NCPDP (SCRIPT + Telecom via `@cosyte/ncpdp`, the
> [NCPDP guide](./guides-ncpdp)); and ASTM (E1394 records + E1381 framing via `@cosyte/astm`, the
> [ASTM guide](./guides-astm)), the spec-clean generation core across all six formats. On top of it,
> [ASTM guide](./guides-astm)), the generation core across all six formats. On top of it,
> **vendor-quirk generation** for HL7 v2, C-CDA, and ASTM (see the [quirk guide](./guides-quirks)). A
> guide is only written once the behavior it documents is shipped and its runnable example passes the
> doc/code-agreement check.
Expand All @@ -36,8 +36,8 @@ corpus.seed; // => 1867

## Verify an artifact round-trips before you trust it

The round-trip harness proves spec-cleanliness by the parser's own judgment: a spec-clean artifact
re-parses with zero warnings and re-serializes byte-identically:
The round-trip harness checks an artifact by the parser's own judgment: it re-parses with zero
warnings and re-serializes byte-identically:

```ts runnable
import { generateAdt, roundTrip } from "@cosyte/synth/hl7";
Expand Down
2 changes: 1 addition & 1 deletion docs-content/guides-quirks.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ a parser tolerates the **realistic vendor deviations** real-world traffic carrie
the right diagnostic when it does. That is what quirk mode generates.

The load-bearing idea: **the quirk vocabulary _is_ the parsers' own profile systems.** A `synth` quirk
deviates the _structure_ of an otherwise spec-clean message so it round-trips through the parser to
deviates the _structure_ of a message that otherwise re-parses with zero warnings so it round-trips through the parser to
**exactly one intended, stable warning code**, the tolerance the corresponding parser profile
(`hl7.defineProfile`, `ccda.defineCcdaProfile`, `astm.defineAstmProfile`) encodes. This is the
**intended-warning contract**: a quirk fixture is never a fiction, it exercises a documented, coded
Expand Down
8 changes: 4 additions & 4 deletions docs-content/guides-x12.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ sidebar_position: 5

The `@cosyte/synth/x12` subpath builds HIPAA **005010** transactions **through `@cosyte/x12`'s domain
builders** (`build837P/I/D`, `build835`, `build271`), so the ISA/GS/ST…SE/GE/IEA envelope, the computed
HL spine, the control numbers, and every segment are the builder's own: each transaction is
**spec-clean by construction** and round-trips through `@cosyte/x12` with **zero warnings**.
HL spine, the control numbers, and every segment are the builder's own: each transaction
round-trips through `@cosyte/x12` with **zero warnings**.
`@cosyte/x12` is an **optional peer dependency**, needed only for this subpath.

The X12 surface emits:
Expand All @@ -22,8 +22,8 @@ The X12 surface emits:

## A claim, spec-clean by construction

The round-trip harness proves spec-cleanliness by `@cosyte/x12`'s own judgment: a spec-clean
interchange re-parses with zero warnings and re-serializes byte-identically:
The round-trip harness checks an interchange by `@cosyte/x12`'s own judgment: it re-parses with
zero warnings and re-serializes byte-identically:

```ts runnable
import { generate837P, roundTrip } from "@cosyte/synth/x12";
Expand Down
7 changes: 4 additions & 3 deletions docs-content/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ sidebar_position: 1

# @cosyte/synth

Generate **deterministic, seedable synthetic healthcare fixtures** (spec-clean HL7 v2, FHIR R4 /
US Core, C-CDA, X12, NCPDP and ASTM) without hand-writing a byte of the wire format, and **without
Generate **deterministic, seedable synthetic healthcare fixtures** (HL7 v2, FHIR R4 / US Core,
C-CDA, X12, NCPDP and ASTM) without hand-writing a byte of the wire format, and **without
any chance the "patient" you just generated is a real person**.

`@cosyte/synth` is a **consumer** of the cosyte parsers, not a parser. It builds each artifact **through
the parser's own builder/serializer** (so the output is spec-clean by construction) and draws every
the parser's own builder/serializer** (so each artifact re-parses through that parser with zero
warnings) and draws every
identifier, name, date, phone, and address from a **guaranteed-non-colliding synthetic source** (SSA
never-issued SSNs, NANP `555-01xx` phones, `example.*` domains, TEST-NET IPs, a synthetic assigning
authority for MRNs, and a shipped clearly-fake name pool). It is a **format/conformance generator, not a
Expand Down
13 changes: 6 additions & 7 deletions docs-content/limitations.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,14 @@ a given release ships; this page is the shape of the whole.

## The promise (narrow, on purpose)

`@cosyte/synth` emits **deterministic, seedable, spec-clean (and, in quirk mode, deliberately
off-spec) synthetic fixtures** across the six Cosyte formats, and **every value it emits is drawn from
`@cosyte/synth` emits **deterministic, seedable synthetic fixtures (and, in quirk mode, deliberately
off-spec ones)** across the six Cosyte formats, and **every value it emits is drawn from
a guaranteed-non-colliding synthetic source.**

- **Spec-clean by construction.** Each artifact is built **through the parser's own
- **Built through the parser.** Each artifact is built **through the parser's own
builder/serializer** (`@cosyte/hl7`'s `buildMessage`, `@cosyte/fhir`'s model + serializer,
`@cosyte/ccda`'s `buildCcda`, the X12/NCPDP/ASTM domain builders), so it is spec-clean by the same
mechanism that makes the parser's emit side spec-clean, and it is proven by feeding the output
straight back into that parser and asserting **zero warnings**. `synth` never hand-writes wire bytes
`@cosyte/ccda`'s `buildCcda`, the X12/NCPDP/ASTM domain builders), and it is checked by feeding the
output straight back into that parser and asserting **zero warnings**. `synth` never hand-writes wire bytes
around a builder. **Who did the proving matters, and it is not the same answer for every format:
see [Who checks the output, and who does not](#who-checks-the-output-and-who-does-not) below.**
- **Synthetic-by-construction.** There is no code path that can emit a name, identifier, date, phone,
Expand Down Expand Up @@ -199,7 +198,7 @@ measured.

## Coverage, and what is deferred

The spec-clean generation core is **feature-complete across all six formats**. Quirk mode and the
The generation core is **feature-complete across all six formats**. Quirk mode and the
`deid` pairing loop ship for a subset; the honest gaps:

- **Vendor-quirk mode** ships for the three richest profile systems: **HL7 v2, C-CDA, ASTM**. Quirk
Expand Down
2 changes: 1 addition & 1 deletion docs-content/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ providers.
## An unsupported request threw

A generator has nothing to tolerate, so it **fails closed**. Asking for a format or quirk this build
cannot produce spec-clean throws a typed `SynthError` with a stable `SYNTH_FATAL_CODES` value, never a
cannot produce throws a typed `SynthError` with a stable `SYNTH_FATAL_CODES` value, never a
hand-written byte workaround:

```ts runnable throws
Expand Down
2 changes: 1 addition & 1 deletion examples/generate-every-format.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Generate one spec-clean artifact in each of the other five formats, from a seed, and round-trip
* Generate one artifact in each of the other five formats, from a seed, and round-trip
* each through its own parser.
*
* FHIR R4 / US Core, C-CDA R2.1, X12 005010, NCPDP (SCRIPT and Telecom) and ASTM: each is built
Expand Down
Loading