Outputs and on-disk layout¶
What each command writes, where, and which files are safe to delete.
Three roots, configured by [dataset] dir, [artifacts] dir, and
[reports] dir:
| Root | Default | Contains | Regenerable? |
|---|---|---|---|
data/ |
data |
parquet: truth, matrices, scores, history | mostly — see below |
artifacts/ |
artifacts |
JSON and parquet: releases, ledgers, studies | partly not |
reports/ |
reports |
markdown and HTML for humans | entirely |
The short version: reports/ is disposable, data/ is rebuildable from the
two SQLite files, and artifacts/ contains history that cannot be recovered.
data/ — the dataset layer¶
Written by build-dataset unless noted.
| File | Contents |
|---|---|
truth_minute.parquet |
QC'd per-minute station observations |
truth_hourly.parquet |
hourly truth, both _inst and _mean semantics |
truth_daily.parquet |
local-day extremes and sums |
forecasts_long.parquet |
canonical long frame of hourly provider forecasts |
daily_long.parquet |
canonical long frame of daily provider forecasts |
minutely_long.parquet |
native provider minutely nowcasts |
hourly_matrix_live.parquet |
the supervised hourly matrix, live provenance |
hourly_matrix_synthetic.parquet |
the supervised hourly matrix, backfilled |
daily_matrix_live.parquet / _synthetic.parquet |
supervised daily matrices |
ensembles.parquet |
written by ingest-ensembles; ensemble spread features |
manifest.json |
per-file SHA-256 plus the dataset fingerprint |
predict_history.parquet |
written by predict and publish; every accepted served forecast |
runs.parquet |
parsed command invocations and maintenance/recovery child stages (record_kind, parent_run_id, failure_kind, nullable failure_details_json component diagnostics); older ledgers remain readable; pruned to 90 days / 50k rows |
scores/ |
written by backtest |
served_forecasts/ |
archived served documents |
predict_history.parquet is not regenerable
It is the record of what the system actually served, and it is the only input
to self-verification. Delete
it and you permanently lose the ability to measure serving-path drift for
that period. Back it up; do not point [predict] history_path at a temp
directory.
New hourly/daily documents carry release IDs only for finite normalized values,
including zero. Null predictions contribute no release to document readiness or
the top-level release list. Cold-start blends remain unattributed, and rainfall
floors that fill their null fields retain the evidence diagnosis. Archived
documents replay verbatim; historical forecast files are not rewritten. Null
points also omit quantiles and their source; source labels require usable
emitted quantiles. With compatible evidence, unavailable fitting for a promoted
selection is diagnosed separately from rainfall attribution loss. Reasons name
the affected product/variable/semantics and distinguish pending truth on already-issued
rows from finite truth on later-issued rows, missing matrices, missing forecast
columns, and absent finite truth. Polling helps only as live issuance advances;
repeating a fixed historical --now never admits future evidence. A fitted
variable with unavailable prediction inputs is not mislabeled as an unavailable
fit, and a surviving attributed finite value keeps the document ready.
The provenance wall is in the filename¶
_live and _synthetic matrices are separate files. This is deliberate: the
provenance is a filesystem property, not merely a runtime check, so mixing them
requires deleting a guard rather than forgetting one. Attempting to pool raises
MixedProvenanceError. See
Methods: verification §7.
data/scores/¶
backtest.scores.load_scores(path) preserves the stored types, including
String columns, so callers can concatenate scores vertically or join to
String keys. load_scores(path, compact=True) encodes repeated text as
categories in memory. Report, dashboard, and selection readers use compact
decoding; numeric scores, per-case quantiles, and the on-disk schema stay
unchanged. Both modes reject mixed provenance unless allow_mixed=True.
scan_score_file(path) defaults to compact categories; passing compact=False
preserves stored column types, matching the eager loader's default.
Readers use parallel="none", low_memory=True and streaming collection.
Serving partitions selected evidence in batches of SCORE_BATCH_ROWS = 65_536
rows. The dashboard collects each archived file once, sequentially, retaining
leaderboards, winners, calibration bins, provenance, and fold metadata, then
releases its full frame before loading the next file. Rendering uses these
summaries without reopening score files. With mcs or legacy, a complete
report shares these summaries with its dashboard and loads each file once.
With seq_mcs, the dashboard retains its independent collection after report
persists final wealth, for two full loads per file overall. PIT/reliability
failures produce local dashboard warnings and retain valid scoring results.
Report skips missing files as absence and forwards genuine score-read faults
so the supplied-summary dashboard retains unreadable-score and unreadable-artifact
warnings. It finishes available outputs and returns nonzero if a genuine fault
occurred or no file loaded successfully. This does not add score reads or change
sequential final-wealth readback.
scores_{product}_{kind}_{window}_{evaluation_id}.parquet
| Component | Values |
|---|---|
product |
hourly, daily, minutely |
kind |
live, synthetic |
window |
expanding, rolling |
evaluation_id |
identity of the backtest run |
The scores frame is deliberately long and dumb: one row per scored case per
method, and no winner declared. Every leaderboard is a downstream group_by,
which is what lets report recompute leaderboards under new statistical rules
without re-running a single fit.
These files are the bulk of the disk footprint. prune-scores manages them.
It does not repair corruption or remove uncataloged evidence. Follow the
manual score recovery procedure
when a read fault persists.
artifacts/ — evidence and identity¶
| Path | Written by | Contents |
|---|---|---|
releases/*.json |
backtest |
immutable ModelRelease content; managed content requires an approval event |
releases/approvals/*.json |
backtest |
committed sequence, actual approved_at, and nullable historical visible_at floor |
active_release.json |
predict, publish |
atomic record of the local output: release_ids (possibly empty) and output_path; older release_id pointers remain readable |
eprocess/{product}_{kind}.json |
report |
accumulated betting wealth per candidate/reference pair |
history/*.parquet |
report |
eleven append-only ledgers (below) |
alignment.json |
alignment |
measured truth semantics per provider |
truth_qc.json |
truth-qc |
neighbour verdicts, shield fit, and optional UTC ISO generated_at refresh timestamp (schema v3) |
drift.json |
report |
provider drift alarms |
observability/ |
predict, publish |
per-serve snapshots |
auto-restore.json |
publish |
last automatic-recovery signature and attempt time |
latest_publish_attempt.json |
publish |
latest candidate status, reason, hold decision, and locally served document |
auto-restore.log |
recover |
detached recovery stdout and stderr |
A handled truth-QC failure during CLI maintenance preserves the previous
truth_qc.json and still permits report/dashboard/catalog refresh. The failed
stage is recorded in data/runs.parquet; maintenance returns its first nonzero
stage result even when report finishes. A refreshed report may therefore use an
older QC verdict. The dashboard shows its refresh time and age, plus a failed
refresh warning. The panel becomes amber after a failed refresh,
when older than 48 hours, or when freshness is unknown; latched drift stays red.
Legacy drift_verdict.as_of_date is displayed as date-only evidence, becoming
stale only when its latest possible refresh time is beyond 48 hours. File mtime
is not evidence of refresh. Chips report independent conditions, so a quiet
verdict remains green while an unknown refresh time is amber. One central amber
alert uses the same assessment. Run records add optional error_detail, a
whitespace-normalized message capped at 400 characters; older ledgers null-fill
it, and error retains the exception class. No historical artifacts need
migration or rewriting.
Warnings display class and detail together when both exist. Maintenance's
parent run retains its options, while the linked QC stage records {"days": N};
unrelated child stages do not inherit the parent's private failure kind.
Child stage arguments contain public options only. Failure kind and handled
detail are passed explicitly; an active exception supplies its class and message
in preference to a handled detail. Details are normalized when persisted.
Build/backtest failures and unexpected exceptions still stop
maintenance. Source changes alter the code fingerprint: generate fresh live
backtest and report evidence with the updated code before rollout.
Benchmark scratch outputs¶
The fitting and score-reader benchmark scripts default to unique UTC
timestamp-and-UUID JSONLs under the repository's ignored reports/benchmarks/.
Validated results include a .summary.json; each written path is printed.
Explicit relative destinations use the caller's working directory.
Existing primary sample or summary destinations are refused
before measuring (exit 2), as are unwritable parents discovered by a temporary
write/flush probe. Mismatch payload destinations are reserved only during
recovery; an existing primary payload sidecar does not block a run.
Exclusive creation protects late collisions too. Publication
failures exit 1, retain possibly incomplete destination files, and attempt one
rescue to unique *-recovery-... scratch paths. If rescue fails, the available
sample JSONL is emitted to stderr. Nothing deletes destination files during
failure handling.
Before Git or workers, each runner exclusively creates and prints a unique
*-journal-*.jsonl in the repository's ignored scratch directory. Each completed
sample is appended and flushed outside measured worker timings. Journals survive
process termination after an acknowledged checkpoint; flush without fsync
does not promise power-loss durability. Interrupted writes may leave a partial
last line. Journals are retained after every run, hold raw possibly unvalidated
samples, and never include a summary. Journal preparation failures exit 2;
checkpoint failures stop measurement and trigger recovery.
Worker/validation failures attempt one rescue of collected samples before
propagating their original exception and traceback. Secondary failures during
serialization, rescue, reporting, journal closure, or another interruption do
not displace the original failure. The printed journal path identifies retained
samples even when recovery cannot complete. Such results omit an unvalidated summary; score mismatches
also retain .payloads.json diagnostics. Recovery files may describe an
incomplete experiment and never imply success. Inspect retained destinations
before removing them manually, and choose a new output path for another run.
Historical research memos describe their original runners; current commands use
these scratch defaults without replacing the committed evidence. See
CLI output semantics.
The history ledgers¶
artifacts/history/ holds eleven append-only parquet ledgers driving dashboard zones
F and H: quality, churn, verdicts, served_quality, eprocess_wealth,
pipeline, provider_health, build_funnel, changes, evaluations, live_gate.
Append-only is the point — they record what was true at the time, so a regression is visible as a trend rather than only as a current value.
Preserve irreplaceable evidence in artifacts/
eprocess/ — a betting e-process is a sequential accumulation. A bet,
once made, is never revised, and deleting the store discards all accumulated
evidence; the gate restarts from zero wealth and re-earns significance from
scratch. (It resets deliberately on a config or code-version change, because
a different implementation is a different null hypothesis — but only then.)
history/ — append-only ledgers. Deleting them erases the past.
releases/ contains immutable content and append-only approval history;
keep it for historical replay. active_release.json describes the locally
served document. Its references also protect legacy score evidence.
Approval events add nullable visible_at for conservative historical visibility;
current decisions follow committed sequence. Selection markers
legacy_context_missing preserve exact-dataset legacy compatibility per slice.
history/live_gate.parquet records gate diagnostics separately from approval
identity. Churn history adds explicit approval clocks and sequences; older
rows are normalized in memory without rewriting their recorded values.
reports/ — for humans¶
Everything here is safe to delete. The producer for each file is explicit
below; report does not regenerate the independently run alignment or truth-QC
studies.
| File | Contents |
|---|---|
dashboard.html |
the nine-zone offline operator console |
leaderboard_{product}_{source}_{window}_{hash}.md |
per-slice leaderboards |
drift.md |
provider drift alarms |
correlation_{variable}.md |
provider error correlation and \(k_{\text{eff}}\) |
alignment.md |
truth-semantics study, written by alignment |
truth_qc.md |
neighbour cross-check and shield fit, written by truth-qc |
pipeline_health.md |
end-to-end freshness and funnel |
selection_churn.md |
how often the served method changes |
Six files have stable names and are overwritten. Leaderboards are
hash-suffixed per evaluation run, so they accumulate — dozens of files is
normal. prune-scores does not touch them (it deletes only scores
parquet files); delete old leaderboard markdown by hand if the directory
bothers you.
dashboard.html is fully self-contained: CSS, vendored Chart.js, and the data
payload are inlined, so it opens from file:// with no server and can be emailed
as a single file. See Operator dashboard.
Retention: prune-scores¶
grounded-weather-forecast prune-scores --dry-run # always look first
grounded-weather-forecast prune-scores
Kept:
- the newest three scores files per group (product × kind × window);
- any evaluation referenced by the active release, regardless of age;
- any evaluation referenced by a release within the last 7 days;
- all catalog rows — the ledgers survive; only the bulky per-case scores go.
If active_release.json is genuinely absent, the newest current approval under
sequence ordering and recent served-document references are protected together.
A corrupt pointer suspends deletion. Explicit empty/degraded pointers do not
trigger the newest-approval fallback.
If any release JSON file is unreadable or is not an object, all published scores
are kept and the diagnostic names the affected paths. A non-active file can
still protect evidence, so pruning resumes only after those files can be
verified. This safeguard also applies to --dry-run and leaves the existing
temporary-recovery and exit-code behavior unchanged.
Approval verification uses typed internal path/category/detail diagnostics with the
existing issues path interface. Unreadable releases/events, invalid metadata,
denied listings, corrupt active pointers and unreadable served archives suspend
published-score deletion through PruneResult.skipped, including dry runs. These
conditions do not authorize excluded releases. Genuine pointer absence alone uses
the newest current approval fallback. Pre-#79 readers require a compatibility
backport before reading events with visible_at; no artifacts are migrated.
PruneResult.verification additively carries a typed clear/suspended result and
recording time. The CLI records it as informational retention_verification
components inside the existing failure_details_json run field, with exit 0 and
retention_clear or retention_suspended. Details are bounded to 400 characters
per component. Optional path preserves full path identity outside that bound;
optional recorded_at records completion of the check. Both are additive fields
in diagnostic JSON entries; older entries remain readable. Coordination failures
retain their original exit/failure metadata.
No new ledger is created. Suspended verification publishes an immutable diagnostic audit at artifacts/history/pruning_verifications/<check-id>.jsonl. This is operational telemetry, separate from scientific evidence and approvals. The dashboard's published-score protection
panel and pipeline report distinguish the last recorded check (time and age)
from the latest attempt. Completed verification survives later exceptions in its
one existing invocation/stage row. Failed/unverified attempts cannot erase a
suspension; legacy rows do not establish a clean check. The dashboard does not
rescan served archives, and a later clear check supersedes a prior suspension.
Pruning still freshly verifies all protection requirements on every invocation.
Each new check has a unique check_id. Additive diagnostic fields audit_path,
issue_count, omitted_count, audit_error, and nullable boolean dry_run accompany that identity. Nullable category and reason_code describe each issue; nullable audit_reason_code identifies the failed publication operation and nullable audit_cause_code records its known underlying cause. Missing legacy cause metadata remains null. Renderers accept only known fixed explanations and never publish raw exception prose. Unknown or missing codes use neutral diagnostic-reference wording. A check
records one summary and at most 20 issue samples, each with the existing
400-character explanatory bound; full stored sample paths remain separate.
Legacy rows remain readable and their rendering is also bounded. Each audit is
UTF-8 JSON Lines schema 1: a header (kind=pruning_verification, check ID,
recording time, dry-run flag), one kind=issue record per affected path with its
category/detail and nullable reason code, and a final kind=complete record carrying issue_count.
The staged file is flushed and synchronized before exclusive hard-link publication;
the directory is synchronized afterward. Immediately after its own exclusive link
succeeds, a check records nullable audit_candidate_path, relative to the artifacts
directory. This means complete but unconfirmed, not successful publication. Only
after synchronization and lock release succeed does audit_path replace that
candidate reference. Catchable post-link failures retain the candidate and failure
reason in the same check summary. Collisions cannot rewrite or adopt an audit.
Unsupported hard links or synchronization failures are explicit best-effort audit
failures. Clear checks need no audit. An unavailable audit is explicit, preserves
samples and suspension, and changes no command exit or cancellation behavior. The
report's additive audit_state is recorded for confirmed publication,
unconfirmed for a linked candidate, unavailable for a failed publication without
a candidate, not_needed for a clear check, or not_recorded for legacy unknown
publication. audit_candidate_path is displayed separately from audit_path.
These are operational diagnostics; the daily-report schema remains version 1.
Audits are kept for 90 days and while referenced by retained run rows, including
unconfirmed audit_candidate_path references. Valid legacy check IDs protect exact
matching complete audits without granting confirmation or rewriting old rows. Pruning
removes only recognized, complete, verifiably older unreferenced files; uncertain
run references, corrupt/future clocks and symlinks prevent their removal. Reference
ledgers are inspected only when expired committed-audit candidates exist. A shared
nonblocking audit lock protects publishers and cleanup. Exact staging names
.<32 lowercase hex>.jsonl.<16 lowercase hex>.tmp may be removed only when regular,
unchanged on revalidation and older than 90 days. Fresh, active, unrecognized or
unverifiable files remain. Cleanup
failures are informational. A file-local revalidation or deletion failure keeps that
file while cleanup continues for independent verified candidates. Uncertain run
references remain a global stop, including staging cleanup. Cleanup returns at most
20 notes, reserving space for an abort summary and an explicit omitted count.
Dry runs can record audits and telemetry but delete
neither audits nor scores. Dashboard rendering never opens the audit archive.
The report adds nullable check_age_minutes from its reporting cutoff and shows
contended/unknown attempt states. Contention alone is informational and clears
no warning; unresolved malformed evidence is amber. Equal start/end clocks use
the last persisted row. Multiple checks are grouped by effective progress identity with check-ID fallback
(legacy unidentified rows use recorded time), independently of their parent attempt. Only a later successful
explicitly clear non-dry-run check clears an outstanding pruning execution failure.
Dry runs can update verification independently. Check metadata precedes recorded
arguments when determining mode; unknown legacy mode does not establish recovery.
Reports add nullable dry_run and latest_attempt_dry_run fields. Identified bounded
progress precedes audit I/O and is replaced for that check after publication,
preserving one check through catchable interruptions without duplicate rows.
No journaling guarantee is made for SIGKILL or power loss. Public path labels and
safe explanations do not replace full internal path/detail evidence.
RetentionVerification.sample_issues() supplies the same normalized, at-most-20
issue projection to CLI, dashboard, reports and diagnostic serialization. Empty
paths remain null, empty/missing categories become verification_invalid, and
absent reasons remain null. Unknown codes keep their original internal values
and receive neutral public wording. Report explanation summaries are deduplicated
in first-seen order; affected paths and complete audit rows are not collapsed.
The legacy full-audit adapter streams every supplied issue, beyond the sample cap.
Verification stores canonical immutable typed issues. Constructor details= and
text-only replacements now mean literal raw text; round-tripping the raw tuple views
does not strip genuine evidence. Labelled legacy input must use the explicit
RetentionVerification.from_legacy_details(...) adapter. This replaces automatic
prefix stripping at the constructor boundary; persisted journal details stay raw
and historical artifacts are not rewritten. Accepted lists and compatible issue objects are copied into immutable canonical
tuples; already canonical tuples are reused. details exposes canonical raw text;
formatted_details adds path/category labels explicitly. Journal reads never
reattach labels to raw text or strip genuine evidence prefixes. Legacy text edits
retain positional metadata, supplied legacy metadata fields override their corresponding
structured projection, and explicit empty details clear issues. Missing metadata
for new positions uses the existing null/default rules. Metadata-only replacement
preserves issues; typed replacement uses the supplied complete issues without
borrowing previous evidence.
A nullable operation in publication summaries distinguishes phases such as
staging_sync, directory_sync, link, and lock_release; renderers combine
that phase with linked-candidate state. A failure before linking is unavailable,
not an unconfirmed publication. Unknown legacy phases are described neutrally.
PruneResult.audit_cleanup additively exposes a typed cleanup result: bounded
issues, abort state, omitted count, recording time, nullable dry_run, and
nullable deleted_audit_count / deleted_staging_count. New invocations record
known counts, incremented only after successful deletion; dry runs record zero.
Legacy missing values remain unknown. Nullable eligible_audit_count and
eligible_staging_count count only unreferenced, unchanged candidates at final
revalidation. Dry runs report these as “would delete” counts, separately from zero
actual deletions; aborted runs expose verified progress only. Nullable
cleanup_completed is derived from the shared typed CleanupOutcome classification
(unknown, completed, incomplete). Contradictory legacy flags are rejected;
recorded deletion counts do not establish overall completion. Unknown summaries
state the final-status uncertainty separately from completed deletions.
Default results emit no execution diagnostic; recorded unknown outcomes use
audit_cleanup_unverified, never audit_cleanup_complete. Cleanup records one
summary even when clean; a failure after deletion preserves completed counts. The immutable diagnostic
batch is captured once, recorded immediately, and reused in the command result.
AuditCleanupRecord atomically binds cleanup result, check ID, and diagnostic batch.
PruneResult.audit_cleanup and audit_cleanup_diagnostics remain readable
compatibility views, and constructor adapters remain available. Metadata-only
copies reuse the record; changed cleanup or verification identity rebinds the batch
once. Explicitly supplied stale batches are rejected. A standalone bound record
retains its check identity when verification is omitted; explicit verification can
change it. Later authoritative batches replace earlier cleanup diagnostics for the
same identified check at journaling, preserving independent checks and unrelated
components. Exceptions without returned outcomes retain captured progress. The CLI
never independently reconstructs the batch. Informational
retention_audit_cleanup run components retain original bounded details and
separate paths in the same invocation/stage row. Nullable operation records the
failed phase, independently of its reason and underlying cause. Captured sample
details are single-line and bounded to 400 characters; original cleanup failure
details remain available internally, outside public rendering. Their exit code is zero; they
change neither failed-run counters nor pruning exits. Canonical cleanup entries own each bounded issue and its optional full original
failure evidence, including nullable error_type. issues and raw_failures are
readable compatibility views; the latter contains only known original failures.
Legacy issue replacement cannot inherit a previous entry’s operation or cause;
explicit issues plus compact failure metadata require a unique order-preserving
match by validated issue content. Sparse associations remain sparse; ambiguous,
excess, or mismatched metadata fails instead of inventing attribution.
The nullable positional aligned_failures view and constructor input permit
lossless reconstruction even with duplicate issues. Aligned input requires equal
length and validates every non-null pair; it cannot accompany nonempty compact
metadata. Without explicit issues, nonempty raw_failures fully replaces
failure-derived entries, truncating old tails while preserving failure roles.
Empty failure metadata does not clear canonical entries;
explicit empty issues still clear them. Omitted typed context
may use verified caller context; explicitly unknown paths remain null. The existing
tuple-returning cleanup adapter emits safe public notes. skipped and protection_skipped retain
their compatibility interfaces. Compatibility notices use structured path/category
metadata; old unstructured strings receive informational wording and never imply
a failed protection check.
The internal/library failure record adds nullable failure_role with values
abort_cause and file_local. Known roles come from capture sites or explicit
legacy issue categories. Unknown raw failures remain unclassified and use
audit_cleanup_failure with fixed neutral public wording. No role is inferred
from an index, operation name or exception text. Canonical entries and compact/
aligned projections preserve this metadata; journal categories encode the role
without a new ledger column. Known abort-cause evidence requires aborted=True,
including during copies. A completed result must explicitly remove or correct
that evidence. Removing an abort-cause sample cannot relabel a remaining file
failure. Existing raw details and paths are retained unchanged.
Verification and cleanup summaries/samples add nullable progress_id inside
failure_details_json, without changing the run-ledger columns. It starts from
the first identified check and survives copies that change check_id.
Replacement uses (stage, progress_id) with legacy check-ID fallback, so final
journal merging and partial-progress capture apply the same rule. A copied result
supersedes its own verification and cleanup batches while independent checks
remain visible. Each new physical verification receives its own progress identity.
Identified batches preserve sample multiplicity; only repeated batch delivery is
replaced. A summary is emitted for a known progress identity even without a check
ID. Reading uses that effective identity before legacy clock grouping and recovers
consistent identity from older summary-less samples; malformed or conflicting
metadata cannot establish a clear check. Structured legacy samples retain their
observed occurrence count without a summary. A shared resolver supplies the same
progress/check fallback at binding, merge, read and serialization boundaries.
progress_id is omitted from JSON only when equal to the row's check_id;
different or progress-only sample identities remain explicit. The wire omission
does not remove internal ownership or combine independent equal-clock checks.
The explicit legacy detail adapter has typed constructor-compatible arguments,
including nullable metadata, without stripping typed or journal evidence. Empty
audit_cleanup_diagnostics=() means omitted; nonempty stale batches remain invalid.
Damaged verification evidence¶
The retention report's boolean verification_unknown is independent of the last
valid check and outstanding execution failure. Invalid identity, clock or count
metadata quarantines its affected group; malformed entries and missing/non-string
stages create uncertainty even when no verification owner can be recovered.
Contradictory retention-stage metadata is also damaged evidence. Valid unrelated
components remain unrelated, and independently valid checks stay visible.
Only a valid clear check recorded strictly after the damaged run's validated ending clock resolves verification uncertainty. Equal-clock and suspended checks do not. A clear dry run can resolve that uncertainty. Separately, a successful apply invocation with an independently valid selected clear check clears an execution failure even if another group in the same row is damaged. The panel remains amber for the unresolved uncertainty until a strictly later clear check. A dry run never clears an execution failure itself.
Both clear and suspended legacy verdict-only rows are verdicts, not issue samples.
Explicitly empty recorded category/path fields on suspended samples retain their
canonical unknown-category/null-path normalization.
A suspended verdict without samples or an explicit count keeps its suspension
and reports an unknown count. Additive nullable issue_count_known metadata
preserves that distinction through diagnostic round trips; false means the count
is unavailable. The report's nullable issue_count and dashboard em dash expose
that uncertainty. Existing integer library views retain zero recovered samples.
Explicit counts, duplicate sample occurrences and omitted counts remain validated.
Ownerless legacy groups preserve their original recorded-clock strings: absent
clocks and equivalent instants written with different offsets remain distinct.
Parsed aware clocks still determine validity, chronological order and cutoffs.
Diagnostic merging and serialization execute inside the recorder's protected
boundary. Progress capture independently contains ordinary diagnostic errors.
Malformed fields retain bounded JSON-safe evidence, with metadata_invalid
marking invalid fields; an invalid_diagnostic_metadata fallback retains a core
run row when optional processing fails. These are informational damage markers,
not execution failures. They cannot replace the command's return or original
exception/cancellation, and do not add ledger columns. Ledger storage remains best
effort. Public warnings use fixed explanations rather than raw diagnostic prose.
Authoritative summary boundaries replace repeated batch delivery while preserving the relative order of every surviving item, including interleaved owners, and all duplicate samples inside a surviving batch. Repeated untrusted deliveries are deduplicated without hashing raw values. Bounded previews never establish that distinct original evidence is equal. Historical artifacts are not rewritten.
Daily report reader¶
Daily-report schema 1 retains its envelope and adds nullable realized-evidence
attribution: evidence_match, evidence_release_id, and
evidence_dataset_fingerprint, plus nullable latest_scored_issue_at. The latter
is present as null even on no-match/error realized slices and also extends verification output and the served-quality ledger; older rows load
with null clocks and no historical migration. Forecast release identities remain separate from
the evidence cohort. Station diagnostics expose the resolved fallback path; latest
run entries carry individual freshness. Served-quality daily deduplication now
includes dataset identity; existing rows and retention bounds remain intact.
Equal evidence/recording clocks prefer the latest scored issue when all tied
candidates have trustworthy clocks, then the forecast dataset; remaining distinct
cohorts produce ambiguous_quality_evidence rather than arbitrary scores.
Method-bearing unattributed slices remain unknown. Realized recorder code is
distinct from evaluation code, and compatible fallback requires strictly older
immutable release content. Malformed relevant run rows produce invalid_run_rows
without discarding independent commands; no completed outcomes at the cutoff use
no_completed_runs_as_of. These are documented schema-1 limitation codes, not a
schema migration.
Backup priority¶
If you back up only some of this:
- The two upstream SQLite files. Everything in
data/derives from them, and the forecast archive in particular cannot be recreated — you cannot ask a provider what it predicted last Tuesday. artifacts/history/andartifacts/eprocess/. Append-only and sequential; not rebuildable.data/predict_history.parquet. The record of what you served.config.toml.
Everything else is a function of those.