Skip to content

Add a result store

Implement ScoreStore when model scores must be persisted in a new database or warehouse.

Persistence-only implementation

Provide:

  • ensure_schema(columns);
  • add_scores(model_id, model_name, plugin_identity, rows, columns);
  • read_run_identity(model_id);
  • get_unscored_variants(variant_ids, model_id);
  • read_score() or an efficient read_scores() override.

The inherited materialize() implementation performs portable annotation loading, score reads, predicate evaluation, and result assembly.

Warehouse implementation

A warehouse may override materialize() to push joins and predicate evaluation into its query engine. The observable result must still match the portable implementation, including missing values, model/source attribution, and three-valued predicate behavior.

Schema rules

Persist shared variant identity, model identity, and the complete plugin run identity separately from declared score fields. Reject a second identity for an existing model ID and reject legacy rows whose identity is unknown or lacks model-instance scoring context rather than assigning them the current wrapper's meaning. The score and detail conformance suites exercise configuration-hash conflicts and incomplete identities. If the backend requires decomposed locus fields, return their scalar dtypes from required_row_identity(); the common scoring engine then validates them before calling the store. The engine derives any omitted portable locus field (chr as str, pos as int, ref and alt as str) from the canonical variant_id and verifies emitted ones against it, so model bindings need not know which store requires them. A store must not redefine those portable dtypes, and any other required field must come from the binding's codec. Allow a reused score column only when its complete logical definition agrees with the existing schema. Reject incompatible collisions before writing data.

Document write idempotency. Callers use get_unscored_variants() as the portable deduplication gate; they must not assume all stores ignore repeated inserts.

Test and register

Subclass ScoreStoreContract, compare materialized output with the in-memory/reference fixture, and test schema evolution, duplicate writes, missing scores, multiple model architectures, and annotation-source integration. Register under altar.score_stores.

Named detail storage

Implement DetailStore separately when the backend should hold binding-declared one-to-many model output. Its contract creates or validates a named schema, finds missing logical row keys, adds rows, streams rows by model/table/variant, and reports the exact stored schema and plugin provenance. Do not add model-specific methods or pair a store with one binding.

Subclass DetailStoreContract and register the adapter under altar.detail_stores. The SQLite reference adapter stores canonical JSON rows behind indexed model, table, variant, and logical-key columns. The BigQuery reference adapter uses the same schema-independent shape in fixed warehouse tables and staged MERGE writes. A backend may instead use native columns only when it can preserve exact schema/plugin collision detection and evolve arbitrary binding-declared schemas without migration ambiguity.