neuroimaging-study-storage.yaml and the modules under neuroimaging-study-storage/ are
the full storage schema: everything we might ever want to represent. It is the source of
truth and is never narrowed in place.
Three axes annotate the storage fields, and they deliberately do not collapse into one. A field can be filled deterministically and still be outside the MVP, or be low priority and inside it.
| Axis | Where it lives | Question it answers |
|---|---|---|
| Does a release represent this field? | in_subset: [mvp] |
Is this in the MVP? |
| How does the field get filled? | in_subset: [deterministic] or in_subset: [model_extracted] |
Does code fill it — API lookup, local store, derived value, generated identifier — or does a language model read it out of the source? |
| How urgently does a human review it? | storage-parameter-priorities.yaml, keyed Class.field → 0–3 or n/a |
Reviewer triage order |
The subsets are declared in neuroimaging-study-storage/subsets.yaml and marked on the
attribute itself:
publication_year:
in_subset: [mvp, deterministic]
range: integer
design:
in_subset: [mvp, model_extracted]Identifiers and type designators are generated by the mapper, so they count as
deterministic whether or not they carry the mark.
python3 check_storage_parameter_priorities.py # every field has exactly one priority entry
python3 check_field_provenance.py # every field says how it gets filled
python3 check_field_provenance.py --strict # ...and none are left unclassifiedcheck_field_provenance.py fails on a field marked both deterministic and
model_extracted, or on an identifier marked model_extracted. Fields not yet classified
are reported as remaining work and only fail under --strict, so the check is usable while
the pass is in progress. It also prints where the marks disagree with n/a in the priority
file, as a second opinion rather than an authority — n/a has carried the same meaning as
deterministic, so a disagreement usually means a field changed hands and the priority
entry has not caught up.
Attributes are the only thing marked. A class has no mark of its own — it survives when a marked attribute's range points at it — so leaving every attribute of a class unmarked is how a whole entity gets dropped. Identifiers and type designators are kept without a mark, but do not by themselves keep a class alive.
python3 gen_mvp_schema.py # writes neuroimaging-study-storage-mvp{.yaml,/}
python3 gen_mvp_schema.py --check # fails when the committed tree is out of dateThe generated tree is committed and must not be hand-edited. The generator refuses to
write a structurally broken schema, reporting instead when a marked attribute points at a
class with nothing marked in it, or when a surviving class drops a required field.
required is only enforced inside classes that survive, so a field required within an
entity we do not extract is not a problem.
Only mvp generates a schema. deterministic and model_extracted label fields across
the whole schema rather than slicing it — the two are interleaved down every path from the
tree root, so pruning to either would strand the other's fields. Each subset declaration
says which it is via a generates_schema annotation, and the generator refuses the ones
that do not.
python3 -m pytest test_storage_schema_modules.py test_mvp_schema.py \
test_field_provenance.py test_review_layer.pyName the files: a bare pytest at the root tries to collect the vendored label-studio/
checkout and errors.
| File | What it holds |
|---|---|
| extraction-readme.md | Rules the schema cannot state: the gates that skip a paper, extraction conventions, validator invariants, mapper responsibilities, and known limits |
| storage-schema-design-notes.md | Why the storage schema is shaped the way it is |
| storage-schema-expressivity-probe.md | Measured expressivity gaps against 25 corpus papers, with options |
| LLM entity identification | Emit |
|---|---|
Group |
local_id, name |
Task |
local_id, name |
Acquisition |
local_id, name |
Preprocessing |
local_id, name |
StatisticalModel |
local_id, name |
Assessment |
local_id, name |
Predictor |
local_id, name |
Condition |
local_id, name |
independent parsing of tables.
| LLM table parsing | Emit |
|---|---|
Analysis |
the coordinates and name of the analysis |
Tasks can have multiple acquisitions, from either simultaneous recordings (EEG+fMRI) for a particular task, or from multiple sites/or the scanner changing during data collection. I am not representing the difference on purpose.