scFAIR and dismech: a schema comparison, and what LinkML would buy both
Analysis date: 2026-09-02. Author: AI-assisted (Claude Code). All schema structure read from source, not from documentation summaries.
Question
The scFAIR consortium (EPFL Deplancke lab + SIB) publishes a single-cell metadata schema that extends CZ CELLxGENE's, and — unusually for a metadata standard — a structured description of computational analysis workflows. dismech independently evolved its own analysis-provenance model for hypothesis exploration runs. This report compares the two, and asks what LinkML modeling would give each.
Companion to bgee-integration-proposal, which
covers the data-layer question (Bgee expression, ASAP's dataset catalogue)
separately. This report is about schemas, not data.
TL;DR
- The two schemas overlap almost exactly on one thing — recording how a computational result was produced — and they are complementary rather than redundant. scFAIR models the happy path in mechanical detail (docker digests, conda environments, random seeds, resource usage). dismech models failure and unverifiability (execution status, auditability, whether a data source was actually accessed). Neither has the other's half.
- Neither side's analysis-provenance model is formally modeled. scFAIR's
schema is prose Markdown plus a 1,798-line imperative validator config.
dismech's
MANIFEST.yamlis validated by hand-written Python and has no schema file at all. dismech's assessment sidecar is LinkML; its execution manifest is not. - scFAIR's
rules.yamlcontains constructs that are, literally, LinkML features written out by hand — a dynamic enum, a conditional rule, a structured pattern, a mixin. The clearest case:assay_ancestor_terms: ['EFO:0030080', …]plus ancestor-checking code isreachable_from, which dismech already uses for itsAssayTermenum rooted atOBI:0000070. - The concrete offer to scFAIR is a LinkML rendering of their schema. The concrete ask is their environment-capture vocabulary. Neither requires the other to adopt the other's model wholesale.
What each side actually is
scFAIR
| Artifact | Form | Size |
|---|---|---|
scFAIR_schema schema/7.1.0/schema.md |
prose Markdown, HTML tables | 1,906 lines |
schema_analysis_json.md |
prose Markdown | 1,324 lines |
schema_atac.md / schema_spatial.md / schema_perturb.md |
prose Markdown | 510 / 356 / 301 |
asap_web config/scfair/7.1.0/rules.yaml |
validator rule config | 1,798 lines |
The Markdown is the normative specification; rules.yaml is what the ASAP
validator executes. They are two hand-maintained expressions of one model. The
schema is a fork of CELLxGENE single-cell-curation 7.1.0, and is CC0.
dismech
| Artifact | Form | Notes |
|---|---|---|
src/dismech/schema/hypothesis_assessment.yaml |
LinkML | 373 lines; classes, slots, enums, slot_usage |
src/dismech/schema/dismech.yaml |
LinkML | the main KB schema, incl. Dataset |
src/dismech/hypothesis_analysis_run.py |
imperative Python | validates MANIFEST.yaml; no schema file |
The split matters: dismech's judgement layer (what an assessor concluded about a provider run) is formally modeled, while its execution record layer — the thing scFAIR also models — is not.
The overlap: analysis provenance
Both schemas describe a computational run. Field-by-field:
| Concept | scFAIR (schema_analysis_json) |
dismech (HypothesisAnalysis / MANIFEST.yaml) |
|---|---|---|
| Run identity | pipeline_name, pipeline_version, pipeline_url, creation_date |
analysis_id, report frontmatter |
| Unit of work | steps[] with step_label, step_category |
one HypothesisAnalysis per analysis |
| Method | method, command |
method, comparison |
| Software | software_version, programming_language, programming_language_version |
HypothesisAnalysisSoftware (software_name, software_version, software_uri) |
| Environment | docker_repo, docker_image_url, docker_image_name, docker_image_digest, conda_env_url, conda_env_file |
environment_artifact (a committed file) |
| Parameters | parameters |
parameters |
| Inputs | inputs[] with label, type, format, location, checksum ("<algo>:<hex>") |
input_data_source_ids → HypothesisDataSource (uri, checksum, byte_count, version, retrieved_at) |
| Outputs | outputs[], same shape as inputs |
output_artifacts, code_artifacts; MANIFEST output roles {CODE, ENVIRONMENT, TABULAR_RESULT} |
| Determinism | random_seed |
— |
| Cost | resources, execution_duration_seconds |
— |
| Timing | execution_timestamp |
retrieved_at, assessed_at |
| Did it work? | — | status: SUCCEEDED / PARTIAL / FAILED / SKIPPED / REPORTED_ONLY |
| Can it be audited? | — | auditability: REPRODUCIBLE / PARTIALLY_AUDITABLE / UNVERIFIABLE |
| Was the data really accessed? | — | access_status: ACCESSED / SEARCHED_NO_RESULT / CITED_NOT_ACCESSED / UNVERIFIABLE |
| Failure lineage | — | status_reason, fallback_from_analysis_id, limitations |
The asymmetry is the finding
scFAIR's step model has no execution-status field. A step is described as though it ran. That is entirely reasonable for its use case — a pipeline description attached to a deposited dataset, written by the person who ran it, where the artifact's existence is the evidence of execution.
dismech's use case is the opposite and is why those enums exist: it assesses
reports written by AI research providers, where the central risk is a report
claiming a computation that never ran. REPORTED_ONLY exists precisely to name
"the report says it computed this; nothing establishes that it did."
CITED_NOT_ACCESSED names "the provider cited a dataset it never opened."
As single-cell analysis pipelines increasingly get generated or driven by agents,
scFAIR will face the same problem, and the fields it lacks are the ones that
catch it. Conversely, dismech's environment capture is a single
environment_artifact file where scFAIR has a container digest and a conda
specification — scFAIR's is straightforwardly better and dismech should adopt it.
What LinkML would buy scFAIR
rules.yaml is a well-organised imperative config. But several of its sections
are LinkML language features written out by hand, which means they need bespoke
validator code, cannot generate anything, and drift from the Markdown spec.
1. Dynamic enums (reachable_from)
rules.yaml carries:
cross_field:
assay_ancestor_terms: ['EFO:0030080', 'EFO:0007045', 'EFO:0002761',
'EFO:0008919', 'EFO:0010184', 'EFO:0008994']
— a list of ontology roots, plus Ruby that walks ancestors to check membership. That is exactly LinkML's dynamic enum, which dismech already uses:
enums:
AssayTerm:
reachable_from:
source_nodes:
- OBI:0000070 ## assay
is_direct: false
relationship_types:
- rdfs:subClassOf
The declarative form is checkable by linkml-term-validator against the live
ontology, cacheable, and generates its own documentation. dismech runs this over
its whole KB today.
2. Conditional rules (rules / preconditions)
organism_cell_type_prefixes:
NCBITaxon:6239: ['CL', 'WBbt']
NCBITaxon:7955: ['CL', 'ZFA']
NCBITaxon:7227: ['CL', 'FBbt']
organism_dev_stage_mapping:
NCBITaxon:9606: HsapDv
NCBITaxon:10090: MmusDv
"If organism is X, the cell-type term must come from ontology Y" is a LinkML
class-level rules block with a precondition/postcondition pair — expressible
declaratively and compilable into JSON Schema conditionals and SHACL shapes. The
cross_field CF-1..CF-9 rules are all of this shape.
3. Structured patterns
The "<algorithm>:<hex>" checksum format and CURIE formats are pattern /
structured_pattern slots. dismech enforces the same thing with a hand-written
regex (^sha256:[0-9a-f]{64}$) — both sides would benefit.
4. Mixins and profiles
schema_atac.md, schema_spatial.md, schema_perturb.md are separate documents
describing additive field sets, with spatial_extension and perturb_extension
blocks in rules.yaml. That is LinkML mixins — one class per extension,
composed into a profile, with no document duplication and no risk of the base
spec and the extension drifting.
5. One source, many targets
The decisive argument. A LinkML schema generates JSON Schema, SHACL, OWL, SQL
DDL, Python dataclasses, and documentation from one YAML file. scFAIR currently
maintains the Markdown spec and rules.yaml in parallel by hand, and the
analysis JSON has no machine-readable schema at all despite being JSON — so
nothing validates it. A LinkML rendering would emit the JSON Schema for free.
Caveat worth stating plainly: LinkML would not express everything in
rules.yaml. The fix_form field groups, UI popup text, and the compliance
report's check taxonomy are presentation concerns that belong outside a data
model — LinkML annotations can carry some, but the honest answer is that
rules.yaml would shrink, not vanish. And a migration is real work on a schema
that currently functions.
What dismech should take
- Model
MANIFEST.yamlin LinkML. It is the one dismech provenance structure with no schema — validated by ~400 lines of imperative Python that re-implements required-field checking, type checking, enum checking, and pattern matching that LinkML generates. The output roles{CODE, ENVIRONMENT, TABULAR_RESULT}are an enum;sha256:<hex>is astructured_pattern; the input/output records are classes. The genuinely bespoke parts — checksum recomputation against files on disk, symlink rejection, sensitive-key redaction — stay in Python, as they should. - Adopt scFAIR's environment vocabulary.
docker_image_digest+conda_env_fileis a stronger claim than "an environment file exists", and it costs nothing to add alongsideenvironment_artifact. - Adopt
random_seed. dismech's replay gate checks that saved code reproduces committed outputs. For any stochastic method that is underdetermined without a seed, and dismech has no slot for one. - Consider
execution_duration_seconds/resourcesas weak signals of whether a claimed computation plausibly ran — relevant toREPORTED_ONLYtriage, though not decisive.
What to propose to scFAIR
- Offer a LinkML rendering of schema 7.1.0, starting with the analysis JSON because it currently has no machine-readable schema and would gain the most. CC0 licensing makes this straightforward. Monarch has deep LinkML expertise and this is a natural contribution.
- Propose execution-status and auditability fields for the step model, with dismech's enums as a starting vocabulary. Frame it as agent-generated pipeline readiness, which is the case that makes it urgent rather than theoretical.
- Align on SKOS mappings rather than merging. The two schemas serve
different scopes and should not converge.
exact_mappings/close_mappingsbetween the overlapping slots would let a dismechHypothesisAnalysisand a scFAIR pipeline step be translated without either adopting the other's model.
Open questions
- Does scFAIR want a formal schema language at all? Forking CELLxGENE means inheriting its Markdown-spec convention, and there may be a deliberate reason to stay aligned with it. This should be asked, not assumed — the answer decides whether the LinkML offer is welcome or an imposition.
- Is CELLxGENE upstream the better target? If CELLxGENE's own schema were LinkML, scFAIR's fork would inherit it. That is a much larger conversation and may be where the leverage actually is.
- How does this relate to the Cell Annotation Schema? CAS is listed as part of scFAIR and is already partly formalized. It may be the precedent for a LinkML rendering, or may already cover some of this ground — worth checking before proposing anything.
- Does dismech's MANIFEST modeling belong in the main schema or a sidecar?
It is not KB content, so probably a sidecar alongside
hypothesis_assessment.yaml. That is a design-register question.
Sources
- scFAIR_schema — schema 7.1.0, CC0
- ASAP validator
rules.yaml - scFAIR Consortium preprint (bioRxiv, 2026)
- LinkML: an open data modeling framework, GigaScience
- dismech:
src/dismech/schema/hypothesis_assessment.yaml,src/dismech/hypothesis_analysis_run.py,docs/hypothesis-report-assessments.md