Chapter 3: Serialization, Identifiers, and Schema Validation

The previous chapter described the protocol record as a graph of typed structures in memory. A record that exists only in memory, however, cannot be reviewed by an institutional biosafety committee, cannot be sent to a collaborating institution, and cannot be produced two years later in response to an audit request. The record has to be written down in a form that two independent programs agree on, given a name that survives every later revision, and checked against a schema before anyone is allowed to act on it. That is the subject of this chapter: the wire format, the identifier, and the validation gate that stands between a submitted document and the record store.

This chapter owns serialization and identifiers for the whole book. Call signatures belong to Chapter 4, lifecycle states and timestamps to Chapter 5, transport and deployment to Chapter 6, and evidence packaging to Chapter 7. Where a topic touches one of those, this chapter states only the part that is a property of the document on disk.

3.1 The Canonical JSON Document

WIA-BIO-014 §8.3.1 presents the protocol as a single JSON object. JSON is a good choice for this record class for unglamorous reasons: it is specified by a short, stable document (RFC 8259), every language in a research institution's software estate can parse it, it survives being pasted into a ticket, and it is readable by the compliance officer who has to sign the record without installing anything. The cost is that JSON has almost no semantics of its own — it cannot express a required field, an enumerated value, a unit, or an ordering rule. Everything that makes the document trustworthy therefore lives outside the document, in the schema and the validator described later in this chapter.

The canonical document is a single object whose members are the aggregate root's identity fields followed by one member per child structure. The spec's example names those members protocol_id, version, target, guide_rna, cas_system, delivery and validation. The reference SDK describes the same record with camelCase TypeScript properties — id, version, target, guideRNA, casSystem, delivery, strategy, validation, safety, parameters. The two are not the same vocabulary, and the difference is not only capitalization: the JSON example carries no strategy and no safety member at all, and it names the identity field protocol_id where the SDK uses id.

An implementation that silently guesses at this correspondence will produce records that round-trip inside one codebase and lose fields at every boundary. The rule adopted in this book is therefore explicit: a conformant implementation publishes a field-mapping table as part of its documentation, and the mapping is code-generated from, or tested against, the schema rather than maintained by hand. The table below is the mapping this book uses; an institution that extends the record extends the table in the same commit.

Table 3.1 — JSON member to SDK property mapping for the protocol record
JSON memberSDK propertyType in the documentRequiredNote
protocol_ididstringyesImmutable across versions; see §3.2
versionversionstringyesRecord version, not standard version
createdcreatedstring (date-time)yesSDK type is Date; serialize per §3.7
updatedupdatedstring (date-time)yesSet by the store, never by the client
targettargetobjectyesTargetInfo
target.genetarget.genestringyesSymbol as written by the submitter
target.gene_idtarget.geneIdstringnoNamespace-qualified external identifier
target.coordinatestarget.coordinatesobjectyesGenomicCoordinates; flattened in the spec example
target.coordinates.assembly…assemblystringyesSpec example calls this genome
target.region_typetarget.regionTypeenum stringyesControlled vocabulary, §3.3
target.purposetarget.purposeenum stringyesControlled vocabulary, §3.3
guide_rnaguideRNAobjectyesRecord fields only; no design guidance in this book
guide_rna.on_target_score…onTargetScorenumberyesRequires a tool-and-version provenance note
cas_systemcasSystemobjectyestype, variant, source are enums
deliverydeliveryobjectyesTyped envelope plus open parameters
delivery.cell_typedelivery.cellTypestringyesFree text unless the institution binds a vocabulary
strategystrategyobjectyes (SDK)Absent from the spec's JSON example
validationvalidationarray of objectyesObject in the spec example, array in the SDK
safetysafetyobjectyes (SDK)Absent from the spec's JSON example
parametersparameters?objectnoOpen map; see the units rule in §2.4

The three bold rows are the practical work of adopting this standard. The published example is not a subset of the SDK type; it is an older or looser shape. Two reconciliations are possible, and an institution must choose one and write the choice down. The first is to treat the SDK type as authoritative and the example as illustrative, which is the position this book takes: strategy and safety are required members, and validation is an array so that a protocol can carry more than one validation record. The second is to accept the spec shape on ingest and normalize it, in which case the normalizer is itself a piece of conformance-relevant software and its behaviour on the three divergent members must be covered by test vectors (Chapter 7).

The annotated example below is written in the shape this book uses. Every value that the published sources filled with a plausible-looking biological or instrument value has been replaced with an obvious placeholder in angle brackets. That is deliberate, and it is the second rule of this section: an example document must not be mistakable for a recommended configuration. A reader who copies the example gets a document that fails validation until they supply their own values, which is the correct outcome.

{
  "protocol_id": "<INST>-CRISPR-<YYYY>-<NNNN>",
  "version": "3",
  "created": "2026-01-15T09:20:00+09:00",
  "updated": "2026-02-02T14:05:11+09:00",
  "target": {
    "gene": "<GENE_SYMBOL>",
    "gene_id": "ensembl:<ENSG_ID>",
    "coordinates": {
      "chromosome": "<CHR>",
      "start": 0,
      "end": 0,
      "strand": "+",
      "assembly": "<ASSEMBLY_LABEL>"
    },
    "region_type": "exon",
    "purpose": "knockout",
    "description": "<one line, no subject-identifying text>"
  },
  "guide_rna": {
    "sequence": "<RECORDED_BY_SUBMITTER>",
    "pam": "<PAM_LABEL>",
    "on_target_score": null,
    "specificity_score": null,
    "provenance": {
      "tool": "<tool name>",
      "tool_version": "<semver or build id>",
      "computed": "2026-01-15T09:18:42+09:00"
    }
  },
  "cas_system": {
    "type": "Custom",
    "variant": "Custom",
    "source": "recombinant"
  },
  "delivery": {
    "method": "other",
    "cell_type": "<cell line or primary cell label>",
    "parameters": {
      "<param_name>": { "value": null, "unit": "<unit>", "source": "<SOP id or instrument log>" }
    }
  },
  "strategy": {
    "type": "NHEJ",
    "expected_outcome": "<free text, as written by the submitter>",
    "timeline": "<free text>"
  },
  "validation": [
    {
      "method": "NGS",
      "description": "<free text>",
      "timing": null,
      "parameters": { "<param_name>": { "value": null, "unit": "<unit>" } }
    }
  ],
  "safety": {
    "off_target_analysis": false,
    "ethics_approval": false,
    "approval_refs": [],
    "biosafety": "BSL2",
    "regulatory": [],
    "risk_level": "low",
    "mitigation": []
  },
  "schema": {
    "standard": "WIA-BIO-014",
    "standard_version": "1.0",
    "profile": "<institution profile name>@<profile version>"
  }
}

Three details in that document are worth naming. Numeric fields that have not been measured are null, not 0; a zero in a score field is a claim, and a false one. The guide_rna.provenance object is an addition this book recommends rather than something the published type carries — any stored number that a program computed needs the tool and version that computed it, or the number cannot be reproduced or retracted. And the schema member records which standard version and which institutional profile the document was written against, which is what lets a reader five years from now validate the document with the right schema rather than the current one.

3.2 Identifier Design and Immutability

The identifier is the part of the record that other systems copy. Sample inventories, sequencing job tickets, committee minutes, purchase orders and publication supplements will all end up holding a protocol identifier as a string, and none of them will update it. The identifier therefore has one hard requirement that overrides every aesthetic preference: it never changes, and it never gets reused.

The published sources show an identifier of the general shape CRISPR-<year>-<sequence>. That shape is serviceable inside one institution and fails as soon as two institutions exchange records, because both will mint a CRISPR-2026-001. The profile this book recommends prefixes the institution and separates the record identity from the record version:

protocol-id   = institution "-" domain "-" year "-" serial
institution   = 2*16 ( ALPHA / DIGIT )     ; registered institutional prefix
domain        = "CRISPR"                    ; fixed for WIA-BIO-014 records
year          = 4DIGIT                      ; year the record was first created
serial        = 3*8DIGIT                    ; zero-padded, monotonic per institution-year
version       = 1*DIGIT                     ; separate field, never part of protocol-id
reference     = protocol-id [ "@" version ] ; how other systems cite a specific version

The grammar above is an illustrative profile, not a normative requirement of WIA-BIO-014; the standard constrains protocol_id only as a string. What matters is that the institution pins one grammar, validates it in the schema with a pattern, and records the grammar in the profile manifest so that a partner can tell a malformed identifier from an unfamiliar one.

An alternative worth considering is an opaque identifier — a UUID (RFC 9562) or another random token — with the human-readable label carried in a separate display field. Opaque identifiers cannot collide across institutions, cannot be guessed, and cannot silently encode information that later turns out to be wrong. Their cost is that nobody can read them aloud, and that a mistyped identifier produces a lookup failure rather than a visibly wrong string. A reasonable compromise, and the one most research-administration systems converge on, is an opaque primary key in the store with a structured, unique display identifier that the schema also constrains.

Versioning of the record is the other half of identity, and it is where most implementations go wrong. The temptation is to treat a protocol record as a row that gets edited: a reviewer notices a wrong assembly label, someone fixes the field, the updated timestamp moves, and the record now describes something different from what the committee approved. The rule this book states plainly is that a corrected record is a new version, and the previous version stays readable forever.

Table 3.2 — What changes, what does not, and what it produces
Situationprotocol_idversionResult
Typo in a free-text description, record still in draftunchangedunchangedDraft edit; audit event records the field change
Any change after the record left draftunchangedincrementedNew version; prior version retained and citable
Wrong value discovered after approvalunchangedincrementedCorrection version, with a reason and an approval reference
The project changes target or intentnewstarts at 1New protocol, with a supersedes pointer to the old one
Record created in error, never executedunchangedincrementedWithdrawn version; the record is not deleted
Two institutions merge their registriesunchangedunchangedPrefixes keep both readable; no renumbering

The version field is a record counter and has nothing to do with the version of the standard or of the SDK. Conflating the two is a common and expensive mistake: a store that writes "version": "1.0" into every record — as the spec's example does — has thrown away the ability to cite a specific revision, and every downstream citation becomes ambiguous. Use a plain integer counter for the record version (serialized as a digit string, as in the example above), keep the standard version in the schema member, and let Chapter 8 handle semantic versioning of the standard itself.

3.3 Controlled Vocabularies as Code

A large part of what makes these records comparable is that certain fields may only hold values from a fixed list. The SDK expresses those lists as TypeScript union types, which means the compiler enforces them in one language and nothing enforces them anywhere else. To make them enforceable at the document boundary they have to be restated as schema enumerations, and to make them reportable they have to be treated as a registry with its own change process — the idea formalized for metadata registries in ISO/IEC 11179.

Two rules apply to every vocabulary in this standard. First, enumerated values are stored in English in both the Korean and the English edition of a record. The stored token is an identifier, not display text; translating it inside the document breaks every query, every join and every exported report. Localization happens in the user interface, from a label table keyed by the token. Second, an escape value is not a free pass. Several vocabularies here provide one — Custom, other, Other — and a record that uses it must carry the intended value in an adjacent free-text field, because an escape value with no explanation is an unanswerable question a year later.

Table 3.3 — Controlled vocabularies in the protocol record
FieldVocabulary (SDK type)Escape valueReporting use
cas_system.typeCasSystemTypeCustomGrouping records by system family
cas_system.variantvariant unionCustomDistinguishing revisions of one family
cas_system.sourcesource unionnoneProcurement and inventory joins
guide_rna.pamPAMTypeNoneLabel only; no design meaning in this book
strategy.typeEditTypenonePortfolio reporting by intent
target.region_typeregion unionotherSearch facet
target.purposepurpose unionnoneApproval routing (Chapter 5)
delivery.methodmethod unionotherLabel only; no parameter advice
validation[].methodmethod unionOtherEvidence completeness checks
safety.biosafetyBSL1|BSL2|BSL3|BSL4noneStored label for reporting only
safety.risk_levellow|medium|highnoneReview queue prioritization
organism (request-level)OrganismcustomReference-data selection

The safety.biosafety row deserves the emphasis it gets in this book's scope statement. In the record it is a four-value string and nothing more: a label that says which containment designation the institution assigned to the work. It is not a facility specification, it does not imply any procedure, and a validator must never infer one from it. What software may legitimately do with the label is route the record to the right reviewer, filter a report, and refuse a transition when the label and the approval reference disagree.

Adding a value to a vocabulary is a schema change, and it is the additive kind: old documents remain valid, old readers encounter a token they do not know. That is survivable only if readers are written to treat an unknown enumerated value as "unrecognized, preserve and flag" rather than as invalid input. Removing or renaming a value is a breaking change, handled in §3.6.

3.4 Three Layers of Validation

"Valid" is not one question. A document can be perfectly formed JSON with a missing approval reference, or carry every required field while claiming coordinates from a different genome assembly than the one it names. Separating validation into layers is what lets an implementation say precisely what it checked — which is the difference between a validator that is useful as evidence and one that merely returns true.

Table 3.4 — Validation layers, what each decides, and where the standard's error codes land
LayerQuestion it answersMechanismError codes (SDK CRISPRErrorCode)
1 — Structural Is this a well-formed WIA-BIO-014 document with all required members, correct types and legal enumerated values? JSON Schema (2020-12) or the OpenAPI 3.0 schema object; runs without any reference data C004 INVALID_SEQUENCE, C007 INVALID_PARAMETERS
2 — Referential Do the values resolve? Does the assembly label exist, do the coordinates fall inside it, does every referenced identifier exist in the system it names? Lookups against reference data and sibling registries; needs network or a local mirror C008 GENOME_NOT_FOUND, C009 OFF_TARGET_ANALYSIS_FAILED
3 — Policy Is the institution allowed to proceed? Is an unexpired approval referenced, is the required attestation present, is the requested transition permitted for this submitter? Institutional rules plus lifecycle state (Chapter 5); configurable, versioned, and auditable C006 ETHICS_VIOLATION
(not a record-validation layer) Codes that belong to scoring and design functions rather than to record validation (the reference implementation does not actually raise any of them; Chapter 4 §4.5) Out of this book's scope by the global exclusions; documented as codes only C001, C002, C003, C005

Two honest notes about that table. The spec's §8.4 error table lists six codes; the SDK's enumeration lists ten, and the four extra ones are the ones an implementer actually needs for record validation. This book uses the SDK's set, and Chapter 4 gives the full code-to-caller-action mapping. The assignment of codes to layers, by contrast, is this book's profile: the published sources do not classify their codes, and an institution that disagrees with the assignment should publish its own mapping rather than leave callers to guess. C010 VALIDATION_FAILED is deliberately left out of the layers: this book reserves it for "the validator could not run", which is an operational fault in whichever layer it occurs, not a finding about the record (Chapter 4 §4.5).

The layering has one operational consequence that is easy to state and easy to violate: a validation result must record which layers ran. A structural-only pass reported as "validated" is the single most common way a conformance claim becomes false, because the policy layer — the one the committee cares about — is also the expensive one to run, and it is the first thing a busy pipeline disables. A result that names its layers makes the omission visible instead of invisible.

An error payload should therefore say where the failure is, which layer found it, and what the caller can do. The shape below follows the conventions of RFC 9457 problem details, with a JSON Pointer (RFC 6901) locating the offending member:

{
  "type": "https://wiastandards.example/errors/wia-bio-014/validation",
  "title": "Protocol record failed validation",
  "status": 422,
  "protocol_id": "<INST>-CRISPR-2026-0007",
  "record_version": "3",
  "validator": { "name": "<validator name>", "version": "<semver>", "policy_config": "<profile>@<version>" },
  "layers_run": ["structural", "referential", "policy"],
  "is_valid": false,
  "errors": [
    {
      "code": "C007",
      "layer": "structural",
      "pointer": "/validation/0/timing",
      "detail": "Required member is null; supply a value or remove the validation entry.",
      "caller_action": "fix_document"
    },
    {
      "code": "C006",
      "layer": "policy",
      "pointer": "/safety/approval_refs",
      "detail": "No unexpired approval reference for the requested transition to in_execution.",
      "caller_action": "obtain_approval"
    }
  ],
  "warnings": [
    {
      "layer": "referential",
      "pointer": "/target/coordinates/assembly",
      "detail": "Assembly label resolved from a local mirror dated <date>; confirm currency."
    }
  ],
  "validated": "2026-02-02T14:05:11+09:00"
}

The validator object is the part that makes this payload evidence rather than a log line. A validation result that does not say which validator, which version and which policy configuration produced it cannot be reproduced, and an irreproducible pass is worth very little when a reviewer asks why a record was accepted. Chapter 7 treats the signed form of this payload as an artifact in the evidence package.

3.5 Validator Constants Are Software Defaults, Not Recommendations

The reference SDK exports a frozen object named CRISPR_CONSTANTS. Its members include OPTIMAL_GC_MIN, OPTIMAL_GC_MAX, MAX_POLY_T, MIN_ON_TARGET_SCORE, MIN_SPECIFICITY_SCORE, MAX_OFF_TARGETS, BE_WINDOW_START, BE_WINDOW_END, PE_PBS_LENGTH, and four members whose names begin with TYPICAL_. The validator compares submitted values against several of them and emits warnings or errors accordingly.

This book states the following about those constants, plainly and once, because a reader who misunderstands this will misuse the standard. They are unsourced software defaults. The specification's reference section for scientific literature is empty, so no published basis for any of the numeric values is available in the standard's own documents. They are not scientific recommendations, they are not design thresholds, they are not acceptance criteria, and no value in that object should be read as guidance about how to plan an experiment. What they are is the configuration of one reference implementation's warning behaviour — and configuration belongs in a configuration file, under version control, owned by the institution.

The practical instruction is to externalize them. A validator that reads its thresholds from a named, versioned policy configuration can (a) be audited, because the configuration is a file with a history, (b) be reproduced, because a validation result cites the configuration version, and (c) be corrected without a code release. A validator with the numbers compiled in can do none of these things, and worse, it makes its defaults look like the standard's requirements.

{
  "policy_config": "<institution>-crispr-record-policy",
  "policy_version": "2026.1",
  "owner": "<role, e.g. research data governance>",
  "basis": "Institutional decision; the standard supplies no sourced values for these keys.",
  "thresholds": {
    "OPTIMAL_GC_MIN":        { "value": null, "action": "warn",  "set_by": "<committee or role>" },
    "OPTIMAL_GC_MAX":        { "value": null, "action": "warn",  "set_by": "<committee or role>" },
    "MAX_POLY_T":            { "value": null, "action": "warn",  "set_by": "<committee or role>" },
    "MIN_ON_TARGET_SCORE":   { "value": null, "action": "warn",  "set_by": "<committee or role>" },
    "MIN_SPECIFICITY_SCORE": { "value": null, "action": "warn",  "set_by": "<committee or role>" },
    "MAX_OFF_TARGETS":       { "value": null, "action": "warn",  "set_by": "<committee or role>" }
  },
  "record_completeness": {
    "require_approval_ref_before_execution": true,
    "require_provenance_for_computed_scores": true,
    "require_denominator_with_percentage": true
  }
}

Note what is null in that file and what is not. The numeric thresholds are left empty because this book has no sourced value to put there and will not invent one; the institution fills them or removes the key. The three completeness rules, by contrast, are set to true, because they are record-keeping requirements that follow from the data model rather than claims about biology. That is the line this chapter asks implementers to hold: software may enforce whatever the institution decides about its own records, and must not dress an institutional default as a scientific fact.

One further consequence: because these thresholds produce warnings rather than structural failures, they must not gate a lifecycle transition on their own. A warning that blocks a transition is an error with bad manners, and an error whose basis is an unsourced default is an audit finding waiting to happen.

3.6 Schema Evolution

Any record format that outlives one project will change. The useful distinction is between changes that old readers survive and changes that break them, and the discipline is to know which one you are making before you merge it.

Table 3.5 — Classes of schema change
ChangeClassEffect on existing recordsRequired action
Add an optional memberAdditiveRemain validMinor schema version; note in changelog
Add a value to a vocabularyAdditiveRemain validMinor version; readers must tolerate unknown tokens
Relax a constraint (widen a range, drop a pattern)AdditiveRemain validMinor version
Add a required memberBreakingEvery existing record becomes invalidMajor version, or add as optional with a documented default
Rename a memberBreakingReaders lose the field silentlyMajor version; never rename in place
Remove a memberBreakingData loss on re-serializationDeprecate first, remove at the next major version
Change a member's typeBreakingParse or coercion failuresMajor version; new member name is usually safer
Remove or rename a vocabulary valueBreakingExisting records hold an illegal tokenMajor version plus a documented mapping
Tighten a constraintBreaking in effectSome existing records become invalidTreat as breaking; survey the corpus first

The rule that covers most of the table is short: rename or remove implies a version bump, and a member name is never reused for a different meaning. The reason to be strict about the second half is that a reused name produces records that validate and lie, which is worse than records that fail. One source detail needs noting: the Annex E schema-evolution note in the PHASE documents says that renaming or removing a field "MUST trigger a minor version bump". This book's profile classifies those changes as breaking and therefore major, following the Annex H meaning of a major version; an institution should record which of the two readings it applies.

The companion rule is about the reader rather than the schema. When a validator or a store encounters a member it does not know, it has three choices: reject the document, drop the member, or preserve it. Only the third is acceptable for a record that will be exchanged between institutions. Dropping unknown members is silent data loss, and it is loss that surfaces at the worst possible time — when the partner who sent the record asks why the field they populated is missing from the copy you returned. An implementation should keep unknown members in a preserved extension area, round-trip them byte-for-byte where possible, and report their presence as a warning rather than an error. Where an institution needs to add fields of its own, a reserved prefix convention such as x_, or a single nested extensions object, keeps local additions from colliding with future standard members.

Migration between versions is a record-producing activity in its own right. A record rewritten to a new schema version is a new record version with the migration tool and its version cited, and the pre-migration document is retained. Chapter 5 covers the audit event that migration writes; Chapter 8 covers how the standard itself is versioned and deprecated.

3.7 Round-Trip and Encoding Pitfalls

The failures in this section are not exotic. They are the ones that quietly corrupt a record corpus over a few years, and every one of them has been observed in research-data systems of exactly this shape.

Percentages stored without their denominators. The record model carries both read counts and the percentages derived from them. JSON numbers are typically parsed as IEEE 754 binary64 doubles, so a percentage is an approximation of a ratio, and an approximation cannot be re-checked. Worse, a percentage with no denominator hides its own reliability: the same stored value can come from a handful of reads or a very large number, and no reader can tell the difference. The rule stated in Chapter 2 and enforced here as a completeness check is that a computed percentage is never stored without the numerator and denominator that produced it, and that the percentage is treated as a convenience for display rather than as the record of the measurement.

Date objects serialized without an offset. The SDK types declare created, updated and ValidationResult.validated as Date. On the wire they must be RFC 3339 date-time strings with an explicit UTC offset. A timestamp written as a local wall-clock string is unorderable the moment a second site touches the record, and a timestamp written as a numeric epoch is unreadable to the human reviewer. Store the offset, keep the precision you actually have, and never accept a client-supplied value for updated — Chapter 5 explains why the store owns that field.

Non-ASCII text that is not normalized. Free-text members — descriptions, cell-type labels, mitigation notes — will contain Korean, accented Latin, and occasionally Greek letters used in gene and protein naming. Documents must be UTF-8 (RFC 3629), and text destined for comparison or search should be normalized, conventionally to NFC as described in Unicode Annex #15. Without normalization, two visually identical labels compare unequal, deduplication fails, and a search for a record returns nothing. This is also the place to enforce the privacy rule from Chapter 2: free-text members are exactly where subject-identifying text tends to appear, and a schema cannot detect it. A submission-time check plus a reviewer's attention is the only control that works.

Lossy re-export. Sooner or later someone will export the corpus to a spreadsheet for a report. CSV (RFC 4180) has no nesting, no types, no null distinct from empty, and no way to represent an array of validation records except by inventing a convention. An export is a one-way projection and must be labelled as such: derived, dated, not a record. The failure mode to guard against is the round trip — an exported sheet edited by hand and re-imported, arriving with numbers reformatted by the spreadsheet, leading zeros stripped from identifiers, and dates silently rewritten to the exporter's locale. If hand editing is a real requirement, the intake path is a generated template that the validator accepts, not a re-import of an export.

Digests over non-canonical JSON. Later chapters hash documents: the evidence envelope of Chapter 4, the audit-event chain of Chapter 5 and the evidence package of Chapter 7 all bind content by SHA-256 digest. Two serializations of the same record differ in key order, whitespace and number formatting, and a digest over either is stable only by accident. Whenever a digest is taken over a JSON document, the document is first canonicalized with the JSON Canonicalization Scheme (RFC 8785), and the canonicalization method is recorded beside the digest. This is the canonical-JSON rule the later chapters refer to.

Table 3.6 — Serialization pitfalls and the rule that prevents each
PitfallSymptom when it bitesRule
Percentage without denominatorA result cannot be re-derived or defendedStore counts; treat the percentage as display
Zero used for "not measured"Reports average in values that were never takennull for absent, zero only for measured zero
Timestamp without offsetAudit events cannot be ordered across sitesRFC 3339 with explicit offset, store-assigned
Unnormalized UnicodeDuplicate records; search missesUTF-8 plus NFC before comparison
Unknown members dropped on readPartner's fields vanish from the returned copyPreserve and warn; never silently discard
Identifier reused or renumberedCitations in minutes and tickets point at the wrong recordIdentifiers are immutable and never reused
Enumerated value translated in the documentQueries and joins fail across editionsTokens in English; translate in the interface
Example values copied as configurationPlaceholder text becomes a real recordExamples use visibly invalid placeholders
CSV round tripIdentifiers and dates silently rewrittenExport is derived and one-way

3.8 Section Summary

A protocol record becomes usable outside the program that made it only when three things are pinned: the document shape, the identifier, and the validation gate. The document shape requires an explicit mapping between the JSON member names and the SDK properties, including the three members where the published sources disagree with each other, and requires examples whose values cannot be mistaken for recommendations. The identifier must be institution-scoped, immutable and never reused, with the record version kept as a separate field and a correction expressed as a new version rather than an edit. The validation gate must be layered into structural, referential and policy checks, must report which layers ran, and must cite the validator and policy configuration that produced the result. Everything numeric that the reference implementation ships as a threshold is an unsourced software default and belongs in an institutional configuration file, never in prose that a reader could mistake for guidance. Chapter 4 takes the same record and describes the API surface that produces and checks it.

Scientific and laboratory-safety content. This chapter covers record structure, identifiers and schema validation only. For scientific, biosafety or regulatory questions, consult your institutional biosafety committee (IBC) and institutional review board (IRB), and the official biosafety and regulatory authorities — in Korea the KDCA and the authorities administering the LMO Act, and internationally the WHO, the NIH Guidelines and your national regulator.