TextValue and record-type audit
These are historical, read-only production observations from 2026-09-21 using source schema 11.23.0. They informed the projection but do not replace its validation rules. Public evidence and the original acceptance discussion are in issue #14. Its original retained-child-table implementation was corrected in #17 and PR #18.
TextValue content
The audit derived paths from Database collections, induced slots, subclasses,
and inlined objects. It read every document in the five applicable collections,
inspected complete TextValue objects including undeclared keys, and expanded
arrays. It was not a sample or a query limited to has_raw_value.
| Collection | Documents read | Schema TextValue paths | TextValue objects |
|---|---|---|---|
biosample_set |
27,352 | 134 | 67,760 |
field_research_site_set |
110 | 4 | 0 |
material_processing_set |
35,234 | 2 | 0 |
organism_sample_set |
0 | 1 | 0 |
organism_set |
0 | 1 | 0 |
| Total | 62,696 | 142 | 67,760 |
Every observed TextValue had exactly has_raw_value and its expected TextValue
type discriminator. All raw values were nonempty strings. No language or
unexpected keys were present, even with null/empty values. There were no missing
raw values, malformed scalar wrappers, or empty objects/lists at these paths.
Thus, saying “only has_raw_value was populated” would omit the populated
wrapper type. What the audit established was that no additional content would
be lost beyond that redundant wrapper discriminator. The runtime still rejects
populated extra content, unexpected discriminators, and non-string raw values.
Of the 142 schema paths, 101 are single-valued and 41 are multivalued. Thirty-one biosample slots were populated. Ten multivalued slots supplied 3,571 arrays with one element each in this scan. That observation does not remove schema-declared multiplicity: the current projection preserves all occurrences in parent string-array columns.
The scan ran from 21:53:40 to 21:53:49 UTC. Exact cursor counts matched estimated collection counts before and after scanning. Reads were sequential rather than a point-in-time snapshot. The other 14 schema collections have no reachable TextValue-ranged slots and were excluded by schema. Runtime-only collections and malformed TextValues at undeclared paths were outside scope. Key presence and population were counted separately; zero, false, and whitespace count as populated, while null and empty string/list/object values do not.
The source materialized schema SHA-256 was:
ac85605b838789207eba0717dcde2d89c9dc2c312479fcc1cdd469cee8a8fe30
The audit script and count-only reports were retained locally as
audit_textvalues.py, report.json, and type_report.json under the dated
nmdc-textvalue-audit-2026-09-21 audit directory. They are not packaged product
files. No production record values, identifiers, credentials, or connection
strings are included in this documentation or the synthetic test fixtures.
Polymorphic collections
The source schema declares these four collection ranges with multiple concrete types. A separate full aggregation from 21:55:40 to 21:55:41 UTC counted their record types:
| Collection | Declared source class | Concrete types in schema | Types observed | Records |
|---|---|---|---|---|
configuration_set |
Configuration |
2 | 2 | 20 |
data_generation_set |
DataGeneration |
2 | 2 | 24,676 |
material_processing_set |
MaterialProcessing |
11 | 8 | 35,234 |
workflow_execution_set |
WorkflowExecution |
11 | 11 | 34,973 |
| Total | 94,903 |
All 94,903 records had an expected type in the collection hierarchy; none had
missing/null or unexpected types. For example, DataGeneration contained
MassSpectrometry and NucleotideSequencing. Configuration records were
ChromatographyConfiguration or MassSpectrometryConfiguration. Schema classes
can be supported even when no production instances exist: Culturing,
Isolation, and MixingProcess were absent from the material-processing counts.
All 19 source collection ranges require a type slot. Its treatment depends on
where the value occurs:
| Location | Projection behavior |
|---|---|
| Primary collection record | Keep the original type value and use it to select subclass fields. |
| Non-TextValue child-table record | Keep that child record's own type. |
| TextValue wrapper | Validate its discriminator, then extract the raw string without a wrapper type column. |
| Single-valued embedded object | Existing expansion uses the type for dispatch but omits that embedded type column. |
The generated primary type column remains required data. It does not retain
LinkML's designates_type: true: values such as nmdc:Biosample identify source
classes, not the generated target class BiosampleFlat.
Synthetic checks covered all source subtypes across all 19 collections and confirmed primary type retention. Generated schemas union subtype columns, including both supported embedded expansion levels, with subtype-only columns optional. The generator correction also addresses the nested-dispatch mechanism reported in #11; that issue still tracks the separate Agent source-schema upgrade, fixtures, release, and downstream rollout.