Architecture#
Scarf uses concrete domain packages and a one-way module-load dependency structure. The public API is exposed through package facades, while reusable computation and storage mechanics live in focused implementation modules.
Dependency direction#
Module-load dependencies point toward the earlier layers in this list:
Foundation:
storage,matrix, andutilsData model:
metadata,assay, andgraphDomain algorithms:
neighbors,embeddings,clustering,trajectory,metrics,features,quality_control, andmappingImport and export:
readers,writers, andmergeOrchestration:
datastoreanddatastore._operationsPresentation:
plotting
Root facade#
The root scarf package is a public import facade.
It does not form another runtime layer and must not eagerly import the implementation graph.
Only modules listed by that facade are available as lazy root attributes.
Presentation and lower-level algorithm packages such as plotting, clustering, neighbors, and trajectory require explicit imports.
This keeps the root surface small and avoids loading optional presentation code.
Plotting and local imports#
plotting does not import datastore.
Unified plotting consumes a narrow datastore adapter instead of resolving projection paths.
Existing heatmap functions still read Zarr-backed values from their duck-typed store and assay inputs.
Removing that remaining storage coupling is deferred.
Function-local imports may cross toward presentation when a public method explicitly requests a plot. Examples include RNA feature selection and datastore quality-control helpers. These calls do not create module-load cycles.
Package responsibilities#
Foundation#
storage/owns stores, layouts, schemas, arrays, sharding, copying, resource budgets, storage profiles, materialization, ANN persistence, selection snapshots, run/stage records, Zarr runtime guards, and artifact lineage reports.matrix/owns the lazy blockwise matrix abstraction used over NumPy and Zarr arrays. Its arithmetic, indexing, and reduction behavior keeps it separate from low-level storage mechanics.utils/owns generic array, compute, logging, prefetch, process, and progress helpers. Zarr-specific helpers belong instorage, notutils.
Facade aliases do not change implementation ownership.
scarf.utils.load_zarr remains available for compatibility, but its implementation belongs to storage.stores.
Column prefetch uses storage.parallel because read-ahead limits and I/O concurrency are governed by the active storage budget.
Data model#
metadata/owns Zarr-backed metadata tables, row streaming, and table queries. It is shared by datastore cell metadata and assay feature metadata, so neitherdatastore,assay, norstorageowns it. Shared value-selection contracts also live here so domain, orchestration, and presentation code can use one typed contract without reversing dependencies.assay/owns normalization, blockwise feature-summary computation, and the RNA, ATAC, and ADT assay types.DataStoreowns planning and persistence of feature-summary artifacts; a bareAssay.score_featuresremains computation-only.graph/owns graph feature projection through named artifact inputs and rejects encoded-path inputs. Analysis execution follows explicit artifact references and must not resolve inputs by parsing encoded paths or choosing an implicit result.
Data-model modules may call domain algorithms from the method that needs them. They must not import those packages at module load time.
Domain algorithms#
neighbors/owns ANN construction, KNN queries, graph operations, diffusion, and weighted-neighbor integration. It does not own stored KNN graph arrays; persistence lives indatastoreandstorage.embeddings/owns PCA, LSI, Harmony correction, UMAP, SG-tSNE, embedding initialization, and the narrow storage adapter for imported coordinate artifacts.clustering/owns Leiden clustering and PARIS hierarchy operations.trajectory/owns pseudotime scoring, feature-profile aggregation, feature module clustering, and pseudotime result records.metrics/owns LISI, silhouette, graph, concordance, and integration scores.features/owns variability selection, LOWESS trend fitting, feature scoring, enrichment, rank and regression marker searches, GFF parsing, genomic intervals, and coordinate-based feature construction. It also owns presentation-independent feature resolution and normalized value fetching used by datastore workflows and plots.quality_control/owns filtering, HTO demultiplexing, doublet processing, cell-cycle assignment, and the default cell-cycle gene references.mapping/owns reference artifacts, feature alignment, confidence, Symphony-style correction, and mapping results.
Domain algorithm packages must not import datastore, plotting, or general import/export packages at module load time.
A domain that persists an artifact may use a narrow, named storage adapter.
Import and export#
cytebase/lists, downloads, and opens public datasets.readers/parses supported input formats.writers/materializes Scarf stores and exports supported formats.merge/combines assays and datasets without importingDataStoreduring normal module loading.
Readers parse, writers materialize, and merge combines. Format-specific code belongs in a module named for that format.
Orchestration#
DataStore remains the primary workflow API.
Its public class chain is kept for compatibility:
BaseDataStore
-> GraphDataStore
-> MappingDatastore
-> DataStore
Method implementations are grouped by responsibility under datastore._operations:
graph
embeddings
clustering
trajectory
mapping
mapping_reference
quality_control
features
integration_metrics
presentation
Shared helpers under the same package include enrichment_store and paris_persistence.
Operation mixins have no runtime inheritance from datastore facades, no __init__, and no runtime imports of sibling operation mixins.
TYPE_CHECKING imports of siblings are allowed.
Reusable algorithms must be placed in their domain package before being exposed through a datastore method.
datastore.pipeline_accessor orchestrates the fixed basic RNA recipe. Focused internal modules own
recipe validation, run/stage ledger bookkeeping, filtering, frozen field assembly, and cluster
decision persistence. The reusable bounded silhouette comparison lives in
metrics.cluster_selection; its datastore adapter validates graph-coordinate lineage and persists
the immutable decision. metrics.cluster_selection is not part of the public scarf.metrics
facade. datastore.pipeline_run exposes the narrow durable PipelineRun handle and its frozen
cell and feature views. Pipeline execution creates immutable artifacts and a strict run/stage
ledger under pipeline/runs; it does not write live metadata. The ledger starts stages in recipe
order and can run one on a worker thread (utils.background) while later stages run; it writes
every record and callback from the calling thread. DataStore-owned plotting, marker
loading, and export consume narrow frozen-run views. Completed runs can be reopened by their
immutable label or exact run ID.
agent/ owns the optional single-RNA workflow. Its lazy root facade exposes analyze_rna,
AutomatedWorkflowResult, and AnalysisError. Standalone scientific agent contracts and runners
remain in their concrete packages: data_enrichment, experimental_context, and
biological_interpretation. parameter_tuning has no standalone runner; it owns the candidate
contracts, execution, diagnostics, and selection checks used by the orchestrator’s RNA tuning
stage. Internal modules import concrete owners rather than the root facade. agent/tools/
contains only infrastructure shared by more than one agent.
The orchestration stage history is the sole owner of the immutable request, scientific evidence,
choices, checks, work reservations, recovery, and final artifact references. Checkpoints belong
to their exact stage inputs; they do not create a second workflow lifecycle. The result is a small
address that resolves this history. report/ renders one replaceable analysis page from saved
evidence. It does not call a provider or recompute scientific results.
Presentation#
plotting/ owns the reusable plotting APIs.
It has no import dependency on datastore.
The removed scarf.plots, scarf.plotting._legacy, and DataStore.plot_* APIs must not be restored.
New plots should return the established plotting result types, accept documented data contracts, and use narrow adapters instead of adding storage-path knowledge.
The single-RNA agent keeps its bounded final-map display in agent/_plots.py. This limited report
view reads exact final artifact references, samples only displayed coordinates and labels, and
returns the existing public PlotResult and provenance types. It introduces no core plotting API
or module-load dependency from plotting to datastore.
DataStore.plots is a thin, store-bound accessor over the canonical store-first functions in scarf.plotting.
The accessor imports concrete plot implementations only when a method is called, so this convenience namespace does not reverse the dependency from plotting to datastore.
Public facade policy#
Lazy and eager facades#
The lazy facades in scarf, features, readers, writers, merge, utils, neighbors, clustering, embeddings, trajectory, and plotting are architectural boundaries, not temporary deprecation shims.
Their documented 1.x exports preserve stable import paths and defer optional or expensive implementations until an export is accessed.
The assay, mapping, matrix, metadata, metrics, and quality_control package initializers are eager domain facades.
API reference pages and public contract tests define which of their exports carry compatibility guarantees.
Reloading a lazy facade clears cached exports before resolving them again. This keeps reload behavior deterministic for tests and interactive work.
Private facade exports used by repository tests are patch seams, not additions to the documented user API. New production code should import its canonical implementation directly unless it intentionally needs a public patch seam.
Breaking-release compatibility policy#
This release intentionally has no compatibility bridge for the previous live-analysis contract. The complete hard-break inventory is:
AssayStateandIncompatibleAnalysisStateErrorare removed. A store containing{assay}/stateis rejected on open and must be rebuilt. Scarf never reads, migrates, or uses that group to choose a current result.DataStore.pipeline.run()accepts only the documented recipe options and returns a durablePipelineRun. Removed options and prior return values have no aliases or adapters.Feature selection, graph construction, embeddings, clusterings, scores, markers, mapping, and trajectory operations exchange exact
ArtifactRefvalues. Consumers do not parse encoded metadata names, resolve an implicit latest result, or accept a live result column in place of an artifact.Analysis producers do not rewrite live
Icolumns and do not insert clustering, UMAP, score, or marker columns. Callers use artifact loaders, frozen run views, and plotting adapters instead.Public result records use their current artifact-based constructors. Older positional layouts and field sets are unsupported.
Pipeline run and stage records are strict, exact, and unversioned. Adding, removing, or renaming a persisted field in a later release is an accepted hard break. Unknown or incomplete document shapes fail closed.
Mapping references and query projections use only their current exact-lineage contracts.
Integration label metrics are split by input contract.
metric_clisiandmetric_graph_connectivityuse the keywordannotation_columnfor imported cell metadata;metric_label_concordance(first, second, metric=...)compares exact clustering artifacts. Their formerlabel_colnamekeywords and column- or array-based concordance inputs are unsupported.
Compatibility exists only where a current public facade or an explicit file-schema test says it does. There are no silent migrations, implicit compatibility branches, or forwarding shims for retired internal modules. Incompatible stores and artifacts fail with an actionable error.
Placement rules#
Use these rules when adding code:
Put Zarr mechanics in
storage, blockwise matrix behavior inmatrix, and generic operational helpers inutils.Keep metadata and assay focused on table access, normalization, and persistence.
Put reusable computation in a concrete domain package.
Keep domain packages independent of datastore and plotting.
Use a named storage adapter when a domain persists an artifact.
Put parsing in readers, materialization in writers, and combination in merge.
Keep plotting free of datastore imports and use narrow adapters for new storage-backed inputs.
Add compatibility only at an existing public facade.
Use a concrete biological or computational package name. Do not introduce catch-all packages such as
coreoranalysis.
Architecture boundaries are enforced in tests/test_import_architecture.py.
Public imports, result records, facade behavior, and wheel contents have separate contract tests.
Accepted and deferred decisions#
Accepted#
The datastore class chain remains for public compatibility.
Same-path package facades remain part of the public architecture.
A small set of domain modules has narrow storage dependencies for persisted artifacts.
Marker statistics and genomic feature construction live under
features.Pseudotime-specific feature aggregation and module clustering live under
trajectory.feature_dynamics.metadataremains a root data-model package because assays and datastore orchestration both depend on it.Unified plotting uses a datastore adapter instead of reading Zarr paths.
Store-backed plotting is available through the lazy
DataStore.plotsaccessor without moving implementation ownership out ofplotting.Old flat compatibility modules remain deleted.
Deferred#
Deferred to a later structural phase:
The graph and mapping operation modules remain large. Splitting them requires a separate behavioral and performance gate.
metadataandassayretain established convenience methods, with their domain imports deferred to call time.Function-local cycles inside
assayandreadersremain accepted because there are no module-load cycles.Heatmap plotting still reads Zarr-backed values from duck-typed store and assay inputs. Replacing those reads requires a separate plotting adapter design.
Rejected#
A vague
coreoranalysispackage.Restoring forwarding modules for retired private import paths.
Restoring legacy plotting modules or datastore plotting methods.
Moving storage-aware algorithms into datastore orchestration.
Implementation references#
Read Zarr internals for the on-disk implementation boundary and the public API reference for current contracts. Use Contributing for the test, documentation, and review workflow before making a change.