@cc [owner:Yassimba,label:published-structure,test:test_duckdb_run_history_external_contract_at_migration_seam_gives_published_structure] run-history.published-structure
A migrated Run History schema holds exactly six tables: `turbine_runs`, `turbine_table_results`,
`turbine_table_findings`, `turbine_check_results`, `turbine_failing_rows`, and
`turbine_migrations_applied`. Each table has exactly the published columns, in the published order,
with the published NULL rules. Readers query these names with SQL. Only a Scratch Table
(`turbine_verdicts_*`) can also exist, while Row Flags stage.

@cc [owner:Yassimba,label:published-structure] run-history.public-names
A change to a published table or column ships as a new numbered migration that shows each old and
new name, with a Breaking Changes entry in the changelog. A migration file in a released version
never changes.

@cc [owner:Yassimba,label:published-structure,test:test_history_schema_external_contract_at_referential_seam_rejects_orphans] run-history.one-to-many
Each table result has one run (`run_id`). Each check result and each table finding has one table
result (`table_result_id`). The database enforces these three links. A failing row refers to its
check and to its table results by value only, without a database link.

@cc [owner:Yassimba,label:published-structure,test:test_evidence_external_contract_at_history_identity_seam_isolates_each_row] run-history.check-identity
`defined_in` and `check_name` together identify one check in `turbine_check_results` and
`turbine_failing_rows`. `defined_in` is the id of the Data Product or Quality Spec that declares
the check, without a version, so a new contract version keeps the trend and the failing rows of
the check.

@cc [owner:Yassimba,label:published-structure,test:test_evidence_external_contract_at_history_identity_seam_isolates_each_row] run-history.measured-columns
A PASSED, WARNED, or FAILED check result has `rows_tested` and `rows_failed`. A BLOCKED or ERRORED
check result has an empty `measured_value`, `rows_tested`, `rows_failed`, and `pass_limit`.
A check without a limit has an empty `pass_limit`. `pass_limit` and `engine_details` hold JSON.

@cc [owner:Yassimba,label:published-structure,test:test_run_history_external_contract_at_table_seam_stores_plain_values] run-history.plain-values
`turbine_runs.row_scope` holds `all`, `period`, or `new_only`. `turbine_table_results.structure_matches`
holds `yes`, `no`, `cannot_tell`, or is empty when the run did not compare the structure.
`turbine_table_results.status` holds the status of the table result: `PASSED`, `FAILED`, or
`NO_DATA`. `turbine_table_findings.finding_type` holds a finding id, such as
`turbine.validate.column-missing`. The engine enums do not reach these columns.

@cc [owner:Yassimba,label:published-structure,test:test_run_history_external_contract_at_table_seam_stores_utc_times] run-history.utc-times
Each stored time is a UTC time without an offset. Each read gives the time back in UTC.

@cc [owner:Yassimba,label:published-structure,test:test_run_history_workflow_at_cli_seam_stores_the_window_and_the_contract_fingerprint] run-history.window-and-fingerprint
`turbine_table_results.window_start` and `window_end` hold the Check Window that the scan read:
rows from `window_start` up to, but not including, `window_end`. An end with no limit is empty,
and a scan of all rows has two empty ends. `contract_fingerprint` is a digest of the text of the
contract and of each Quality Spec that adds checks to it, so an edit without a new version gives
a new value.

@cc [owner:Yassimba,label:published-structure,test:test_check_duration_decision_table_at_collect_seam] run-history.check-duration
`turbine_check_results.duration_ms` holds the time of a check that ran alone. A check that shared
one scan with other checks has an empty `duration_ms`, so a sum over the checks of a table never
counts one scan more than one time.

@cc [owner:Yassimba,label:published-structure,test:test_run_history_external_contract_at_table_seam_stores_plain_values] run-history.turbine-version
`turbine_runs.turbine_version` holds the release of `turbine-core` that started the run. Turbine
writes it one time, when the run starts, and does not read it.

@cc [owner:Yassimba,label:published-structure,test:test_run_history_reconstructs_any_run_result_it_finished] run-history.check-severity
`turbine_check_results.severity` holds the `severity` of the check, `error` or `warn`, for each
status. So a reader can tell that a passed check fails the run when its limit does not hold.

@cc [owner:Yassimba,label:published-structure,test:test_duckdb_run_history_external_contract_at_migration_seam_gives_published_structure] run-history.one-name-per-idea
One idea has one name in each table. A status is `status` and its explanation is `status_reason`,
on a run, a table result, and a check result. An order of rows is `display_order`. A name ends
in `_name`, an identifier in `_id`, a time in `_at`, a kind from a fixed list in `_type`. The
exceptions are `defined_in`, which holds an id, and `window_start` and `window_end`, which are
the two ends of a period. `level` is the level of a finding; `severity` is the `severity` of a
check. Each table has its columns in one order: keys, what the row is about, when, status, details.
