Skip to content

08 Contracts And Quality

Status: IMPLEMENTED CORE / ONE PROTECTED FAMILY. Check factories and core gates are fixture-tested. Clinical trials now validates an isolated seventeen-model candidate and promotes it atomically; other query families remain in-place and unprotected. Declarative automation is not loaded. Live/consumer state: no production-scale or consumer-path proof. Denoising state: sharp (noise -> rough -> sharp -> converged) Updated: 2026-07-24

Define the data-contract and quality-gate architecture: which checks exist, which block, how schema drift is caught, and how only quality-passing data plus any reviewed curation overlays reach the q_* layer. Slice docs enumerate concrete checks; this doc fixes the policy.

Quality is expressed as Dagster asset checks — they verify properties of an asset and can block downstream execution. (https://docs.dagster.io/guides/test/asset-checks)

Two complementary gate mechanisms (decided)

Section titled “Two complementary gate mechanisms (decided)”
  1. In-run blocking@asset_check(blocking=True) (or AssetCheckSpec(blocking=True)): a failing check stops downstream materialization within the same run. Use inside a producing job. (https://docs.dagster.io/guides/test/asset-checks)
  2. Candidate-generation gating (implemented for clinical trials) — the complete family is frozen and deterministically checked inside the same database transaction that later promotes its stable views. A failing check raises before the serving head can change.
  3. Declarative-automation gating (target, not loaded today)AutomationCondition.all_deps_blocking_checks_passed() on downstream candidate/promotion assets may later gate other event-driven paths. (https://docs.dagster.io/guides/automate/declarative-automation)

In-run blocking alone stops later assets in one selected job, but an in-place producer may already have mutated shared state before its checks execute. It is a control-flow gate, not a last-known-good guarantee. Clinical trials closes that gap with candidate isolation plus one atomic family promotion; every other family still has the in-place limitation. Model-output and verifier failures remain normal pipeline quarantine; they do not create or mutate curation.decision_events.

flowchart TD
  Upstream[Upstream asset materialized]
  ShapeCheck{Schema / shape check}
  EvidenceCheck{Evidence and provenance check}
  LLMCheck{Model-output and verifier checks}
  CurationCheck{Curation review gate<br/>only when override applies}
  Canonical[Canonical asset materializes]
  Query["q_* read model refreshes"]
  Hold["Previous good q_* state remains served"]
  Warn[Warning recorded, publication may continue]

  Upstream --> ShapeCheck
  ShapeCheck -- fail --> Hold
  ShapeCheck -- pass --> EvidenceCheck
  EvidenceCheck -- fail --> Hold
  EvidenceCheck -- pass --> LLMCheck
  LLMCheck -- fail --> Hold
  LLMCheck -- pass --> CurationCheck
  CurationCheck -- fail --> Hold
  CurationCheck -- pass --> Canonical --> Query
  Upstream -. non-blocking anomaly .-> Warn -. metadata only .-> Query
  • Blocking (must pass to publish): schema/shape contract, referential integrity, evidence-presence (every assertion has provenance), immutable source-artifact provenance for document/payload-derived claims, source-authority rules, LLM/schema/verifier gates for model-produced high-impact claims, and curation-review gates for override overlays.
  • Warning only (observed, non-blocking): freshness drift within tolerance, distribution/volume anomalies, source coverage gaps such as abstract-only publication evidence, unresolved low-severity curation conflicts.

Layered checks (decided skeleton; slices extend)

Section titled “Layered checks (decided skeleton; slices extend)”
  • sources.*: reachable, parseable, count-delta in bounds, no duplicate source keys, freshness within SLA.
  • normalized.*: required source identifiers present, enums mapped-or-quarantined, valid dates, provenance retained.
  • evidence.*: output conforms to deterministic/LLM schema, claim has source locator, source authority tier, source artifact version/snapshot where applicable, extractor/prompt/schema/model version recorded.
  • resolution.*: no auto-accept of fuzzy match without deterministic/LLM verifier or allowed exact basis, provenance basis recorded, source authority considered, ambiguous terms routed to the resolution/conflict queue; a curation proposal is created only via the separate curator workflow.
  • curation.*: override/suppression proposal has actor + timestamp + rationale; high-impact effective overlay has human review; rejection has reason; supersession references prior decision.
  • canonical/query.*: only approved/quality-passing assertions present; announcement-vs-confirmed fact semantics preserved; SQL schema contract holds.

For each q_* model: the table/view exists; column names and types match its checked-in contract; every physical column has generated catalog metadata for description, grain, owner, closed enum, version, and provenance policy; every required row/fact resolves to q_provenance. Drift is a breaking-change failure. (See 07-sql-query-layer.md.)

Cross-Slice Gates Discovered from Slice Denoising

Section titled “Cross-Slice Gates Discovered from Slice Denoising”

Regulatory approvals, publications, news, development programs, epidemiology, and commercial intelligence add six shared quality pressures:

  • Source authority gate: source trust is claim-specific. A regulator label can publish approved indication text with high authority; a press release can publish an announcement event but should not silently become confirmed regulatory truth.
  • Source artifact immutability gate: label, publication, filing, registry, API payload, and article claims must preserve the exact artifact version/snapshot/locator, immutable URI, and content hash used for extraction.
  • Announcement-vs-fact gate: event claims must preserve whether the source says something was announced, submitted, accepted, approved, rejected, published, or merely mentioned.
  • Contextual-state gate: development phase, clinical activity, regulatory state, ownership, and historical maximum remain orthogonal, temporal, and reproducible from milestones.
  • Statistical-comparability gate: epidemiology observations compare only when metric, population, geography, period, unit, denominator, and method are compatible.
  • Commercial-observation gate: reported sales preserve product, organisation, geography, period, currency, scale, restatement, and source locator; conversions and summaries remain derived.

These are the cross-slice gates a builder should implement before any q_* model is considered publishable.

Gate idApplies toBlocking whenWarning whenEvidence in docs
schema_contractevery q_* modeltable/view, column, type, or reliable table nullability differs from its checked-in contractadditive column undocumented07-sql-query-layer.md
catalog_contract_coverageterminal query.q_cataloga physical q_* column has no checked-in contract rownone07-sql-query-layer.md
referential_integrityevery q_* model carrying a resolved_*_key(s), canonical_endpoint_key, segment_key, treatment_line_key, or other cross-slice owner keya referenced key value has no matching row in its producing owner q_* modelnullable key absent where optional, or owner row is provisional/supersededowner docs 21-35
provenance_coverageevery provenance-required q_* row/factrequired row/fact has no q_provenance receipt; bundle grain does not expand to complete receiptsprovenance exists but lacks optional supporting source/decision metadataall slice docs
source_locatorevidence claimsno source artifact/row/span locatorlocator exists but source text coverage is partialtrials/publications/news/approvals/SOC
llm_schema_contractinference-produced evidence/resolution/verifier outputscontract revision or acceptance decision missing; parsed output fails schema; invalid enum or required field absentquarantine rate elevated but below blocking threshold06-llm-extraction-and-verification.md
llm_provider_interfaceinference-producing assetsasset bypasses InferenceRuntime or hardcodes vendor SDK/request/schema in pipeline codeprovider adapter has degraded feature parity but contract tests pass06-llm-extraction-and-verification.md
llm_verifier_requiredhigh-impact model-produced claims/candidatesclaim/candidate lacks accepted deterministic/LLM verifier or exact allowed basisverifier confidence low but subject can remain provisional/searchable06-llm-extraction-and-verification.md
llm_prompt_version_pinnedinference-producing assetstask contract/profile/route revision floats or is absentowner metadata is incomplete06-llm-extraction-and-verification.md
llm_cache_idempotencyinference lifecycleone request key maps to different immutable task/input content, or output is reused without a matching accepted policy decisionrejected-attempt churn above threshold06-llm-extraction-and-verification.md
llm_cost_cap_<task>inference attemptscompleted attempt spend exceeds the check’s reviewed window captask approaches budget threshold06-llm-extraction-and-verification.md
source_artifact_immutabilitydocument/API/payload-derived claimsimmutable URI, content hash, or source version/snapshot id missinglive URL present but archive/storage metadata incompleteapprovals/publications/news/org/trials
source_authoritycanonical assertionshigh-impact assertion lacks acceptable source authority or a reviewed curation overlaylower-authority source used with marker20-pipeline-families.md
document_versiondocument-derived claimssource document version/release id missing even if a hash existsversion exists but source family uses weak version semanticsapprovals/publications/news/org/trials
resolution_basisresolved entity linksfuzzy/semantic match publishes without verifier/reviewed-correction/deterministic basisaccepted but low-confidence or staleall entity-resolution sections
curation_effective_statecuration overlays and canonical assertions affected by themsuperseded/rejected/unreviewed high-impact curation decision appears active or changes publicationunresolved low-severity curation conflict exists05-agentic-curation-and-overrides.md
curation_review_statecuration overlayshigh-impact override/suppression lacks human_approved review statelow-risk auto-reviewed subject is due for audit sample05-agentic-curation-and-overrides.md
announcement_semanticsevent claimsannouncement, submission, acceptance, approval, rejection, and mention collapse into one statusstatus is source-specific but mapped to generic enumnews/regulatory/publication slices
temporal_validityorg/SOC/approval/ownership factsdestructive overwrite loses valid-from/valid-to or observed-at statetemporal precision is low/unknownorganisation/SOC/regulatory slices
search_provenancediscovery/search projectionssearch row cannot trace to source q_*, evidence, or content itemsearch index stale but source row still queryable27-slice-discovery-search.md
downstream_boundaryforecasting/TPP supportAPI/saved-model/app-engine state enters core data modeloptional support model lacks downstream owner note28-slice-forecasting-tpp.md
development_program_semanticsprogram/context, milestone, state-history, landscape, and emerging-evidence assetsprogram/context grain drifts; state axes collapse; temporal fold cannot reconcile to milestones; unknown context is defaulted; landscape counts or evidence grains collapsesource context is incomplete but explicitly unknown/provisional33-slice-development-landscapes.md
epidemiology_observation_semanticsepidemiology observations, series, and selected summariesincompatible metric/statistic/unit/denominator/method contexts mix; projected becomes observed; selection loses its source observationoptional strata or uncertainty is absent in the source34-slice-epidemiology.md
commercial_observation_semanticsproduct sales, FX receipts, and commercial summariesperiod/currency/geography grains collide; reported values are overwritten by conversions; restatement chain breaks; forecast becomes actualsource lacks optional market split or comparable prior period35-slice-commercial-intelligence.md

Reusable factories live in src/bioloupe_data/lib/checks/. Dagster asset modules attach those factories to concrete assets; slice docs choose the gate ids and thresholds. This keeps gate semantics consistent while preserving slice-local modelling.

Each factory must accept:

  • asset_key — Dagster asset key being checked;
  • gate_id — one of the cross-slice gate ids above;
  • severityblocking or warning;
  • contract_ref — pointer to a YAML/Python contract object or SQL assertion;
  • slice_name and owner — for metadata and alert routing;
  • optional where_clause / partition_key / sample_strategy for expensive checks.

Each check result must emit metadata:

  • row count checked;
  • failing row count;
  • sample failing keys/locators, capped;
  • contract version;
  • source/evidence/canonical asset keys involved;
  • remediation hint;
  • publication_protected; it is true for the clinical-trials family because the same blocking rules run against an isolated candidate before atomic promotion, and false for current in-place families.

Dagster asset checks can be blocking so failed checks stop downstream assets in the same run; automation conditions can also require all upstream blocking checks to pass before downstream publication. (https://docs.dagster.io/guides/test/asset-checks, https://docs.dagster.io/guides/automate/declarative-automation)

Gate idFactoryPrimary input asset familyDefault severityImplementation sketchRequired fixture
schema_contractmake_schema_contract_checkquery.q_*blockingCompare the live relation to its checked-in contract for existence, columns, SQL types, and table nullability; PostgreSQL view nullability is not treated as authoritative.One deliberately drifted model and one valid model.
catalog_contract_coveragemake_gate_checkquery.q_catalogblockingFail any physical q_* column whose generated row lacks checked-in contract metadata.Registered model and unregistered q_* relation.
referential_integritymake_referential_integrity_checkquery.q_*blockingExplicit consumer-to-owner key declarations left join non-null values to their producing owner q_* model. Nullable unresolved keys remain allowed.Consumer row whose owner key exists, one dangling key, and one nullable-absent key.
provenance_coveragemake_provenance_coverage_checkquery.q_*blockingUse the checked-in contract row-key and provenance policy; left join required rows/facts to q_provenance and fail missing receipts.Published rows with complete bundle, missing bundle, missing fact_path, and partial provenance.
source_locatormake_source_locator_checkevidence.*blocking for canonical-bound claimsAssert evidence claims retain source artifact key and locator shape required by the source family.Claims with API-row locator, document-span locator, and missing locator.
llm_schema_contractplanned lifecycle checkevidence.*, resolution.*blocking for persisted model outputRequire a schema-valid attempt plus matching accepted decision before an inference projection publishes.Valid accepted output, invalid enum, missing field, old-policy decision.
llm_provider_interfacedefinitions/import testevidence.*, resolution.* inference assetsblocking in CI/definitions testsEnsure assets call InferenceRuntime; provider fake tests cover sync + native batch adapters.Fake provider matrix and one asset that imports a vendor SDK directly.
llm_verifier_requiredmake_llm_verifier_required_checkresolution.*, canonical.*blocking for high-impact subjectsRequire accepted deterministic/LLM verifier or exact basis before fuzzy/model-produced links influence canonical/query facts.Exact link, verifier-pass, verifier-fail, unverified fuzzy candidate.
llm_cache_idempotencyplanned lifecycle checkprovenance.inference_*blocking for identity collisions or invalid reuseAssert immutable request/attempt identities and accepted-result reuse under the active acceptance-policy hash. Multiple attempts may legitimately disagree.Accepted reuse, rejected rerun, profile comparison, policy revision.
llm_prompt_version_pinnedcatalog contract testinference-producing assetsblockingReject missing contract/profile revisions and require retained immutable task revisions.Active route, missing revision, in-flight retained revision.
llm_cost_cap_<task>make_llm_cost_cap_checkinference lifecyclewarning by default; blocking if configuredImplemented: sums recent provenance.inference_attempts.cost_usd joined to request task and compares it to the check’s explicit reviewed window cap. It is observability, not a race-safe pre-dispatch budget reservation.Under-cap, near-cap, over-cap batch run.
source_artifact_immutabilitymake_source_artifact_immutability_checkprovenance.source_artifacts, evidence.*blocking for document/payload-derived claimsRequire immutable storage URI, content hash + algorithm, source version/release/snapshot id, and observed/fetched timestamps; reject live-URL-only artifacts.Valid archived artifact, missing hash, live-URL-only artifact, stale re-fetch with changed hash.
source_authoritymake_source_authority_checkcanonical.*blocking for high-impact assertionsValidate claim type against allowed authority tiers; lower-authority facts may publish only with explicit marker or curation override.Approval/news/publication examples with conflicting authority.
document_versionmake_document_version_checkevidence.*, canonical.*blocking for document-derived claimsRequire source-native document version/release/snapshot id before claims can publish; immutability is checked separately.Label/publication/news/registry document examples.
resolution_basismake_resolution_basis_checkresolution.*, canonical.*blocking where fuzzy match impacts canonical identityReject publishable links whose basis is absent, unsupported, stale, or unverified for that subject class.Exact id, cross-reference, fuzzy ambiguous, reviewed curation override cases.
curation_effective_statemake_effective_state_checkcuration.*, canonical.*blockingEnsure rejected/superseded/unreviewed high-impact curation overlays do not appear active; highest-precedence reviewed decision wins.Proposal, human approval, supersede, reject, active overlay.
curation_review_statemake_curation_review_checkcuration.*blocking for high-impact overlaysRequire human_approved for high-impact curation subject kinds; allow not_required only for explicitly low-risk subjects.Pending high-impact proposal, approved override, low-risk auto-reviewed overlay.
announcement_semanticsmake_event_semantics_checkevidence.*, canonical.* event assetsblocking when collapsed status would misleadEnforce source-specific event status vocabulary and prevent announced/submitted/approved/published collapse.News announcement vs FDA approval vs publication accepted cases.
temporal_validitymake_temporal_validity_checkcanonical.* temporal assetsblocking for destructive overwriteRequire valid-from/valid-to/observed-at rules for ownership, labels, SOC assertions, and temporal relationships.Ownership rename/acquisition with overlapping periods.
search_provenancemake_search_provenance_checkquery.q_search_*, query.q_discovery_*blockingImplemented: require every search row’s source_q_model_name / source_q_row_key tuple to name an existing query.q_* relation with a matching q_provenance receipt. Embeddings are outside the current discovery contract.Search row linked to an owner receipt and an orphan source-row tuple.
downstream_boundarymake_downstream_boundary_checksupport query assetsblockingReject columns/tables that encode app runtime state, saved scenarios/models, HTTP routes, or downstream engine ownership.Valid readiness row and invalid saved-scenario/API-shape row.
development_program_semanticsmake_development_program_semantics_checkcanonical.development_program_*, query.q_development_*blockingValidate program/context uniqueness, non-overlapping temporal state, orthogonal clinical/regulatory axes, milestone reconciliation, unknown-context preservation, declared count grain, and source/arm/population evidence grain.The ten acceptance scenarios in 33, including active Phase 2 after terminated Phase 3 and approved plus active expansion.
epidemiology_observation_semanticsmake_epidemiology_observation_semantics_checkcanonical.epidemiology_*, query.q_epidemiology_*blockingValidate metric/statistic/unit context, series comparability signature, periods, denominators, adjustment basis, uncertainty, observed/estimated/projected basis, and selected-summary references.Crude vs adjusted, count vs rate, multi-year period, restatement, and missing-forecast-input cases from 34.
commercial_observation_semanticsmake_commercial_observation_semantics_checkcanonical.product_sales_*, query.q_product_*blockingValidate reporting period kinds, Q1-Q4, currency/scale preservation, FX receipts, geography compatibility, restatement chains, active source-fact uniqueness, and actual-vs-forecast evidence kind.Q4, annual plus Q4, FX, restatement, geography, acquisition, and prose-only cases from 35.

Factory naming is intentionally domain-readable. A builder should be able to scan defs/<layer>/<slice>.py and see the gate vocabulary from this doc attached directly to assets.

Comparative therapeutic-fact gate fixtures

Section titled “Comparative therapeutic-fact gate fixtures”

The first high-value gate fixtures should prove the system distinguishes three comparable-but-not-interchangeable fact rows: registry trial outcomes (21), regulatory label indications (22), and publication outcomes (23).

FixtureExpected blocking/warning behaviorGates exercised
Registry outcome with missing immutable AACT/CTGov snapshotblock publication of q_trial_outcomessource_artifact_immutability, provenance_coverage
Registry outcome with result group but unaccepted arm linkpublish row only with arm_link_state='unlinked'; block if it claims a design armresolution_basis, llm_verifier_required
Label indication with source text but no region/regulatorblock q_approval_indicationsschema_contract, source_authority
CDE translated indication without original language text or translation versionblock translated fieldsdocument_version, source_locator, provenance_coverage
Publication outcome with hazard ratio but no text/table locatorblock q_publication_outcomessource_locator, source_artifact_immutability
Publication outcome linked to trial by title inference onlyblock canonical comparison until verifier acceptance or reviewed curation override; may publish as provisional evidenceresolution_basis, llm_verifier_required, curation_review_state
Same ORR appears in registry and publication with different valuesdo not collapse; publish both with conflicting/supporting provenance rolesprovenance_coverage, source_authority
Approved indication inferred from publication resultblock; publication can support evidence but cannot become regulatory truthsource_authority, downstream_boundary

These fixtures prevent the tempting but wrong simplification into one universal outcome/fact table. The gate question is not “does a numeric value exist?” but “is this value source-authoritative for the fact the query row claims to represent?”

The legacy publication issue trackers are not historical noise; they are a regression taxonomy for greenfield checks. They show repeated failure modes where LLM extraction, post-processing, views, and query rollups produced plausible-looking but clinically wrong facts. Greenfield keeps them as named gate families with fixtures, rather than prose bug anecdotes (../bioloupe-data-gov/docs/publication_issues_tracker.md, ../bioloupe-data-gov/docs/publication_issues_tracker_2.md, ../bioloupe-data-gov/docs/audit_triage_2026_03_30.md).

Methodologically, these are domain-specific data-quality expectations: Dagster asset checks enforce blocking/warning gates on assets, while custom expectation-style checks express domain rules that generic schema tests cannot capture (Dagster asset checks: https://docs.dagster.io/guides/test/asset-checks; Great Expectations custom expectations: https://docs.greatexpectations.io/docs/oss/guides/expectations/creating_custom_expectations/overview/). The taxonomy covers standard quality dimensions such as accuracy, completeness, consistency, validity, and uniqueness, but translates them into pharmaceutical evidence invariants (IBM data-quality dimensions: https://www.ibm.com/think/topics/data-quality-dimensions).

Taxonomy idLegacy evidenceFailure patternGate familyDefault severity
pub_zero_sentinelIssue 8; Issue 54; audit Finding 4unstated values emitted as 0 instead of null/derived value, especially ORR/cORR/Npublication_value_semanticsblocking for published result values
pub_endpoint_semantic_confusionIssues 32, 36, 37, 42, 61TTP→PFS, cORR→ORR, mean→median, tumor shrinkage→RECIST ORR, OR invisible as ORRendpoint_semantic_consistencyblocking
pub_population_denominator_misattributionIssues 26, 40, 45, 57, 60, 62parent population N copied to child subgroup, tested denominator used as positive-subgroup N, disease misattribution, fake armspopulation_grain_integrityblocking
pub_arm_dose_attributionIssues 28, 29, 31, 35, 39, 49, 52, 58dose/escalation/RP2D data attached to wrong arm, all-arms fan-out, intervention/classification arm mismatcharm_dose_attributionblocking
pub_subgroup_extraction_completenessIssues 33, 38, 43, 46, 47, 48, 55, 56cross-tabulated/secondary-analysis biomarker subgroups or sibling endpoints missingsubgroup_endpoint_completenesswarning by default; blocking for target disease slices
pub_safety_attributionIssues 41, 53AE/safety data flattened across dose arms or cohorts without subgroup linkagesafety_attribution_integrityblocking for safety rows
pub_intervention_identityIssue 50non-drug interventions linked as drugsintervention_identity_type_safetyblocking; owner spine 29 supplies target semantics
pub_corpus_identity_uniquenessIssues 17, 18duplicate conference/article records or missing PubMed-indexed publicationpublication_corpus_identitywarning/blocking depending on duplicate/missing evidence impact

Each taxonomy gate must emit metadata that lets a builder or agent diagnose the exact clinical mistake:

Gate familyRequired metadata
publication_value_semanticspublication id/key, endpoint term/key, subgroup/arm key, extracted value, derived candidate value if any, reason zero/null is invalid, source locator.
endpoint_semantic_consistencyraw endpoint phrase, canonical_endpoint_key, conflicting candidate endpoint, endpoint spine evidence, source quote.
population_grain_integrityparent population key/N, child subgroup key/N, numerator/denominator source expression, disease/biomarker segment keys.
arm_dose_attributionsource arm label, normalized arm key, intervention key, dose fields, conflicting sibling-arm values, source quote.
subgroup_endpoint_completenessexpected subgroup/endpoint axes, missing axis, extraction prompt/schema version, source table/text locator.
safety_attribution_integrityAE term/key, grade bucket, cohort/arm key, safety N/value, conflicting cohort/arm source text.
intervention_identity_type_safetysource term, candidate identity type, accepted/rejected resolution basis, drug spine key or NCIt concept key.
publication_corpus_identityDOI/PMID/conference id, title hash, abstract/presentation source ids, duplicate/missing classification basis.

These gates belong in 08/10, not in slice docs, because they cut across publication extraction, endpoint identity (32), drug/intervention identity (29), disease/biomarker identity (30), and query materialization. Slice docs still declare which gate families apply to their assets.

When one of the comparative therapeutic-fact fixtures fails, the check result metadata must be useful enough for an agent or human to understand the broken contract without opening the whole database.

Gate failureRequired metadata
Missing source artifactq_model_name, q_row_key or candidate assertion id, expected source_artifact_key, missing source_content_hash/source_immutable_uri, source family, remediation hint.
Missing source locatorevidence claim id, fact_path, source artifact key, expected locator shape (api_row, json_path, xml_path, pdf_page_span, table_cell, etc.), sample source id.
Bad source authorityassertion id, attempted query model, claim type, actual source_authority_tier, required tiers, source family, downstream risk.
Unaccepted resolution/arm linkresolution candidate id, subject kind, source term, candidate target, basis, verifier state, required basis for publication, whether a reviewed curation override exists.
Missing region/regulatorapproval/indication assertion id, source family, source record key, missing column names, source locator.
Cross-source conflictcompared row keys, model names, fact paths, values, units, source authority tiers, conflict role, whether publication continues as warning.
Previous good retainedprior materialization run id, blocked candidate run id, query model name, affected row count, reason previous state remains served.

Each metadata payload should cap row examples but include enough stable keys for follow-up SQL against q_catalog and q_provenance.

The system must prefer a stale-but-known-good query layer over a fresh-but-unverified one. This state machine is implemented for the clinical-trials family and remains the adoption target for other query families.

stateDiagram-v2
  [*] --> CandidateMaterialized
  CandidateMaterialized --> ChecksPassing: blocking gates pass
  CandidateMaterialized --> HeldBack: any blocking gate fails
  HeldBack --> PreviousPublishedServed: do not refresh q_* contract
  PreviousPublishedServed --> CandidateMaterialized: upstream fix or new curation decision
  ChecksPassing --> Published: atomically replace family serving head
  Published --> [*]

Warning-only checks must never silently disappear: they emit Dagster check metadata, appear in operations dashboards, and can be promoted to blocking by changing the gate contract.

  • Checked-in YAML contracts interpreted against Postgres information_schema are the current schema authority. A contract library may replace the implementation only if it preserves that reviewable, database-backed behavior.
  • Expensive LLM evaluations run on an explicit sampled or separate cadence and record the sample contract. They are never hidden inside ordinary materialization and never replace deterministic blocking checks. Dagster permits check-specific automation conditions for this purpose. (https://docs.dagster.io/guides/automate/declarative-automation/automating-asset-checks)