Quality Report as a first-class run artifact¶
Every pipeline run emits a Quality Report alongside the data: per-Stage metrics (parse confidence distribution, entity/relation counts and confidence histograms, Screening hit rates, Disambiguation merge decisions with their Evidence and confidence), corpus-level KG statistics, and completeness checks that every record satisfies its contract (Provenance/Classification present, offsets aligned). An optional gold-set harness lets an adopter drop in 20–50 annotated documents and get precision/recall per Stage on THEIR corpus. This makes the framework's "high-quality, AI-ready data" claim auditable rather than asserted — the exact gap in the private stack, which filtered on confidence thresholds it never measured. Rejected: public-benchmark scripts alone (say nothing about the client's own run), inline thresholds only (the current unsubstantiated posture), and deferring evaluation past v1 (shipping a data-quality framework with no notion of quality).
The Hardening bar (#73, ADR-0034) extends each StageMetrics with per-Stage typed-error observability (error_count + a stable error_category tag) — Quality Report schema v14 — and pairs the report with a run-scoped tracing-span seam. Both keep this artifact's counts-only, no-PII discipline: a failed Stage records its error category, never the record content that triggered it.
Amendment (H-F1, #108 — the report builder is its own reusable unit). Assembling this artifact used to live inside the local Runner (LocalRunner._build_report + ~15 _*_quality methods, ~830 lines). That made the report a hostage of the executor: the second Runner (the Airflow adapter, ADR-0003/0030) could only produce the same report by reaching past LocalRunner's interface into six private methods (# noqa: SLF001 on _build_report, _final_records, _report_uri, _run_dir, …). The 360 audit's THEME F verdict — "the interface is the reuse surface; if a second impl must reach past it, the shape is wrong" — applies exactly. H-F1 lifts the report half into latence_core.quality_report_builder.QualityReportBuilder, a cohesive unit that depends only on what it needs: a narrow StageRecordReader seam (a Stage's records + a Screening Stage's findings, read from the live outputs dict or its on-disk checkpoint — #69) plus the relation fan-out tally the executor captured, not the whole executor. LocalRunner now composes it (its _build_report/_final_records are thin delegators; it exposes the public reader stage_records/read_findings, the layout accessors run_dir/report_uri, and its composed builder via quality_report_builder); the Airflow adapter builds and persists the report through that public builder, so the report-building SLF001 reaches are gone (the adapter's total # noqa: SLF001 count drops from 13 to 6; the remaining 6 are the still-permitted execution-side reaches — _execute_stage, _drain_retry_count, _output_type, _read_checkpoint_text). This is a pure structural refactor with zero behaviour change: the Quality Report is byte-identical (proved by rebuilding it through a freshly-constructed public builder and asserting byte-equal JSON, and by a fake-reader that is not a LocalRunner at all), no SCHEMA_VERSION change, and determinism/resume/delta/Purge stay green with unchanged test assertions.