Skip to content
Open
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
27 changes: 27 additions & 0 deletions Ideas.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,30 @@ Running list of design ideas to consider for future brainstorms.
together all of a given subject's digital records.

- Allow globally immutable openMinds documents to be linked.

## Follow-on work for treatment consolidation

- Author a canonical `drug_treatment` profile under
`schemas/V_beta/profiles/` that supersedes the retired `treatment_drug.json`
schema. Required fields at minimum: `drug` (ontology), `dose` (quantity,
canonical unit TBD by domain expert — likely `mg_per_kg` or `mg`), `route`
(ontology). Optional: `onset` (relative_quantity, same shape as in
`virus_injection`).

- Author a `mammalian_stereotaxic_virus_injection` profile that `extends:
"virus_injection"` and adds required stereotaxic coordinate fields
(`stereotaxic_ap`, `stereotaxic_ml`, `stereotaxic_dv` as `quantity` with
canonical unit `mm`) plus a `target_region` field constrained to
UBERON:brain descendants.

- Consider applying the same profile mechanism to the stimulus family
(`stimulus_bath`, `stimulus_presentation`, etc.) once it is proven on
treatments. Different domain (stimulus-to-element rather than
treatment-to-subject) but the same `_shape_from_minischema` + `_minischema`
mechanism would apply unchanged.

- Decide and document a canonical-unit convention for cross-profile
coherence: when two profiles measure the same physical quantity, they must
use the same canonical unit (e.g., all volumes in nL, all times in days or
seconds, not mixed). A small registry table in this repo (or the spec)
would help enforce this by review.
87 changes: 64 additions & 23 deletions schemas/V_beta/did_schema_meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,9 +220,12 @@
"matrix",
"timestamp",
"boolean",
"structure"
"structure",
"ontology",
"quantity",
"relative_quantity"
],
"description": "Data type of the field. Uses the standard JSON Schema keyword 'type'; values are NDI-specific."
"description": "Data type of the field. Uses the standard JSON Schema keyword 'type'; values are NDI-specific. The 'ontology' type holds an ontology_object value. The 'quantity' type holds an object {<canonical_unit_label>: double, source_value: double, source_unit: string}; the canonical-unit label is declared in _constraints.canonical_unit_label. The 'relative_quantity' type adds a 'reference' string to the quantity object and declares its reference anchor in _constraints.reference."
},
"_blank_value": {
"description": "Value in a freshly constructed blank document. May fail validation."
Expand Down Expand Up @@ -271,31 +274,69 @@
"$ref": "#/$defs/field_definition"
},
"description": "Nested field definitions for structure type fields."
},
"_shape_from_minischema": {
"type": "boolean",
"description": "Only valid on type 'structure'. When true, the field's nested shape is delegated to a minischema carried on the document instance under the top-level _minischema key; the schema file does not need to list nested _fields. When omitted or false, a structure field must list its own _fields."
}
},
"if": {
"properties": {
"type": {
"const": "structure"
"allOf": [
{
"if": {
"properties": {
"type": {
"const": "structure"
},
"_shape_from_minischema": {
"const": true
}
},
"required": [
"_shape_from_minischema"
]
},
"then": {
"required": [
"_name",
"type",
"_blank_value",
"_default_value",
"_mustBeNonEmpty",
"_mustBeScalar",
"_mustNotHaveNaN",
"_queryable",
"_ontology",
"_documentation",
"_constraints"
]
},
"else": {
"if": {
"properties": {
"type": {
"const": "structure"
}
}
},
"then": {
"required": [
"_name",
"type",
"_blank_value",
"_default_value",
"_mustBeNonEmpty",
"_mustBeScalar",
"_mustNotHaveNaN",
"_queryable",
"_ontology",
"_documentation",
"_constraints",
"_fields"
]
}
}
}
},
"then": {
"required": [
"_name",
"type",
"_blank_value",
"_default_value",
"_mustBeNonEmpty",
"_mustBeScalar",
"_mustNotHaveNaN",
"_queryable",
"_ontology",
"_documentation",
"_constraints",
"_fields"
]
}
]
}
}
}
212 changes: 212 additions & 0 deletions schemas/V_beta/profile_meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://did-schema.example.org/meta/profile_meta.json",
"title": "DID/NDI Profile Meta-Schema",
"description": "Validates the structure of DID/NDI profile (minischema) files. A profile is a named, versioned, ontology-anchored schema fragment that defines the shape of a structure field on another document type. Profiles live under schemas/V_beta/profiles/ (canonical) or as user-defined documents in a database. The inline _fields array reuses the field_definition shape from did_schema_meta.json.",
"type": "object",
"required": [
"profile_name",
"profile_version",
"_maturity_level",
"extends",
"profile_ontology",
"_documentation",
"_fields",
"promoted_fields"
],
"additionalProperties": false,
"properties": {
"profile_name": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$",
"description": "Unique snake_case identifier for this profile."
},
"profile_version": {
"type": "string",
"pattern": "^\\d+\\.\\d+\\.\\d+$",
"description": "Semantic version string MAJOR.MINOR.PATCH. Same semantics as _class_version on regular schemas: MAJOR bumps on breaking changes (removing required fields, changing canonical units, tightening constraints); MINOR on additive changes; PATCH on docs-only changes."
},
"_maturity_level": {
"type": "string",
"enum": [
"work_in_progress",
"mature"
],
"description": "Maturity level of this profile."
},
"extends": {
"type": "string",
"description": "profile_name of a parent profile that this profile extends, or empty string for no parent. When non-empty, consumer tooling flattens the parent's _fields into this profile's _fields (parent-first order) before validating a document's manipulation structure."
},
"profile_ontology": {
"$ref": "#/$defs/ontology_object"
},
"_documentation": {
"type": "string",
"description": "Human-readable description of what this profile represents."
},
"_fields": {
"type": "array",
"items": {
"$ref": "#/$defs/field_definition"
},
"description": "Array of field definitions describing the shape of each named entry inside the manipulation structure of a document conforming to this profile. Same shape as _fields on a regular schema file."
},
"promoted_fields": {
"type": "array",
"items": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
},
"description": "List of field _names from _fields that consumer database tooling should materialize as indexed columns for fast search. Purely an indexing hint; the manipulation structure remains the source of truth."
}
},
"$defs": {
"ontology_object": {
"type": "object",
"required": [
"_namespace",
"_term",
"_name",
"_uri"
],
"additionalProperties": false,
"properties": {
"_namespace": {
"type": "string",
"description": "Ontology name (e.g., uberon, obi, pato, uo, ucum)."
},
"_term": {
"type": "string",
"description": "Term identifier within the namespace."
},
"_name": {
"type": "string",
"description": "Human-readable label of the ontology term."
},
"_uri": {
"type": [
"string",
"null"
],
"description": "Full resolvable URI, or null if unavailable."
}
}
},
"field_definition": {
"type": "object",
"required": [
"_name",
"type",
"_blank_value",
"_default_value",
"_mustBeNonEmpty",
"_mustBeScalar",
"_mustNotHaveNaN",
"_queryable",
"_ontology",
"_documentation",
"_constraints"
],
"additionalProperties": false,
"properties": {
"_name": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$",
"description": "Field name (snake_case)."
},
"type": {
"type": "string",
"enum": [
"did_uid",
"char",
"string",
"integer",
"double",
"matrix",
"timestamp",
"boolean",
"structure",
"ontology",
"quantity",
"relative_quantity"
]
},
"_blank_value": {},
"_default_value": {},
"_mustBeNonEmpty": { "type": "boolean" },
"_mustBeScalar": { "type": "boolean" },
"_mustNotHaveNaN": { "type": "boolean" },
"_queryable": { "type": "boolean" },
"_ontology": {
"oneOf": [
{ "$ref": "#/$defs/ontology_object" },
{ "type": "null" }
]
},
"_documentation": { "type": "string" },
"_constraints": {
"type": "object",
"description": "JSON Schema validation keywords and NDI-specific constraint keys. Applicable keys depend on 'type'. For 'ontology': 'descendant_of' (ontology_object) optionally constrains values to a subtree. For 'quantity' and 'relative_quantity': 'canonical_unit' (ontology_object, required) names the canonical unit; 'canonical_unit_label' (string, required) is the snake_case field name used in the document to hold the canonical numeric value; 'minimum'/'maximum' optionally constrain that numeric value. For 'relative_quantity': 'reference' (string, required) names the temporal anchor (e.g., 'session_start', 'surgery', 'birth').",
"properties": {
"descendant_of": { "$ref": "#/$defs/ontology_object" },
"canonical_unit": { "$ref": "#/$defs/ontology_object" },
"canonical_unit_label": {
"type": "string",
"pattern": "^[a-z][a-z0-9_]*$"
},
"reference": { "type": "string" },
"minimum": { "type": "number" },
"maximum": { "type": "number" },
"minLength": { "type": "integer", "minimum": 0 },
"maxLength": { "type": "integer", "minimum": 0 },
"pattern": { "type": "string" },
"enum": { "type": "array" }
}
},
"_fields": {
"type": "array",
"items": { "$ref": "#/$defs/field_definition" }
}
},
"allOf": [
{
"if": {
"properties": { "type": { "const": "ontology" } }
},
"then": {
"properties": {
"_constraints": {
"not": { "required": [ "canonical_unit" ] }
}
}
}
},
{
"if": {
"properties": { "type": { "const": "quantity" } }
},
"then": {
"properties": {
"_constraints": {
"required": [ "canonical_unit", "canonical_unit_label" ]
}
}
}
},
{
"if": {
"properties": { "type": { "const": "relative_quantity" } }
},
"then": {
"properties": {
"_constraints": {
"required": [ "canonical_unit", "canonical_unit_label", "reference" ]
}
}
}
}
]
}
}
}
Loading