k4bench.regression.email¶
k4bench.regression.email ¶
Render the nightly regression report as the e-group email — subject, hidden preheader, and the two MIME bodies (HTML and plain-text/Markdown).
The email reads at two levels, top to bottom:
- Header + compact primary actions (Open dashboard / CI run).
- A status summary — separate Failure / New / Reconfirmed / Watch / coverage counts, never colour alone.
- Needs attention — one compact card per
(detector, platform, sample)run group that has a failure, a new confirmation, or a same-release reconfirmation, worst category first, with only the largest few changes and the ranked candidate PRs for that change window. - Detailed report — reference — the full detector → run-group hierarchy, bounded so a pathological night can't produce a multi-hundred-kilobyte mail (every failure always shown; confirmed rows capped per group and globally with honest "Showing X of Y" omitted counts).
- Coverage / data-quality summary and footer.
Vocabulary is release-scoped and mandatory (see
:class:~k4bench.regression.models.MetricVerdict): New is the first
confirmation for the release being measured; Reconfirmed is a later night
of the same release re-confirming it. The engine has already made that
call — this renderer only reads is_new_confirmation / is_reconfirmed and
never re-infers persistence across releases.
Blame (model-ranked candidate PRs) is best-effort: a missing, malformed, or
mismatched sidecar silently degrades to no ranking and never blocks the mail.
Everything read from blame.json — including URLs — is escaped on the way
into markup; it is file content and model output, not something this process
fetched itself.
EmailSummary
dataclass
¶
EmailSummary(night: str, n_failures: int, n_new: int, n_reconfirmed: int, n_watch: int, groups_total: int, groups_judged: int, groups_reliable: int, groups_unreliable: int, groups_unknown: int)
The night's headline counts, shared by the subject, the summary cells and the coverage roster so all three agree.
coverage_text
property
¶
7/7 groups judged — groups with at least one non-UNKNOWN metric.
This is intentionally derived from verdicts, not the host-reliability tri-state: a reliable first run may still lack enough history to judge, while legacy reports can carry judged metrics without a reliability field.
RankingCard
dataclass
¶
RankingCard(detector: str, platform: str, sample: str, base_release: str | None, onset_release: str, n_signals: int, total_window_signals: int, n_new: int, total_window_new: int, n_reconfirmed: int, total_window_reconfirmed: int, candidates: list[CandidatePR], total_ranked: int, compare_links: list[tuple[str, str]], total_compares: int, reused_from: str | None, assessment: StepAssessment | None = None)
One deduplicated ranking for a rank group
(detector, platform, sample, base_release, onset_release) — rendered
once no matter how many metrics share it.
WindowSection
dataclass
¶
WindowSection(detector: str, platform: str, sample: str, k4h_release: str, base_release: str | None, onset_release: str | None, verdicts: list[MetricVerdict], card: RankingCard | None)
One change window: the confirmed metrics whose change entered in it, and the ranked PRs for that window when the sidecar has them.
Sections come from the verdicts, not from the blame sidecar, so a window with no ranking still lists its metrics instead of vanishing — the window is what the reader has to act on, the ranking only helps.
same_release
property
¶
Both ends in one release: nothing upstream moved, so the cause is benchmark-side or noise.
subject ¶
subject(report: NightlyReport) -> str
State-aware, grammatically correct subject.
Priority: any failure or New confirmation → [ACTION]; else any
Reconfirmed → [RECONFIRMED]; else any Watch → [WATCH]; then missing
or unreliable coverage → [NO DATA] / [INCOMPLETE]; else [OK].
Failure counts lead the regression counts when present.
Source code in k4bench/regression/email.py
preheader ¶
preheader(report: NightlyReport) -> str
Short inbox-preview text expanding the subject with coverage and watch — kept compact enough for common preview panes.
Source code in k4bench/regression/email.py
to_html ¶
to_html(report: NightlyReport, *, dashboard_url: str | None = None, actions_url: str | None = None, blame: BlameReport | None = None, historical_blame: dict[str, BlameReport] | None = None) -> str
Self-contained HTML email body (inline styles only, no CSS/JS).
Source code in k4bench/regression/email.py
to_markdown ¶
to_markdown(report: NightlyReport, *, dashboard_url: str | None = None, actions_url: str | None = None, blame: BlameReport | None = None, historical_blame: dict[str, BlameReport] | None = None) -> str
Plain-text/Markdown MIME alternative — the same important content as the HTML body, useful on its own in a text-only client.