@@ -14,6 +14,11 @@ import { PUBLIC_LOCAL_PATH_SCRUB_PATTERN, PUBLIC_UNSAFE_PATTERN } from "../signa
1414import { deliverRecapToDiscord , deliverRecapToSlack } from "./notify-discord" ;
1515import type { GatePrecisionReport } from "./gate-precision" ;
1616import type { DriftRecapSection } from "./maintainer-recap-drift" ;
17+ // #8372: these three section builders shipped fully implemented + unit-tested but were never composed into
18+ // the delivered digest -- the same "built, tested, never called from production" shape as #6636.
19+ import { buildCalibrationRecapSection } from "./maintainer-recap-calibration" ;
20+ import { buildGateOutcomesRecapSection } from "./maintainer-recap-gate-outcomes" ;
21+ import { buildPerRepoRecapSection } from "./maintainer-recap-per-repo" ;
1722import { buildRoutingRecapSection } from "./maintainer-recap-routing" ;
1823import { REVIEWER_ROUTING_SHADOW_EVENT_TYPE , type RoutingShadowDecision } from "./reviewer-routing" ;
1924import type { OutcomeCalibration } from "./outcome-calibration" ;
@@ -167,10 +172,12 @@ function recapSectionLines(items: string[], fallback: string): string[] {
167172export function formatMaintainerRecap ( report : RecapReport , options : { configDrift ?: DriftRecapSection ; routingShadow ?: { title : string ; lines : string [ ] } } = { } ) : string {
168173 const { totals } = report ;
169174 const rate = totals . gateFalsePositiveRate !== null ? `${ Math . round ( totals . gateFalsePositiveRate * 100 ) } %` : "n/a" ;
170- const perRepoLines = report . repos . map (
171- ( repo ) =>
172- `${ redactRecapLine ( repo . repoFullName ) } — ${ repo . reviewed } reviewed, ${ repo . merged } merged, ${ repo . closed } closed, ${ repo . gateFalsePositives } gate false-positive(s), ${ repo . gateOverrides } override(s), ${ repo . reversals } reversal(s)` ,
173- ) ;
175+ // #8372: the dedicated builder replaces the inline map that duplicated it -- unlike this file's old copy,
176+ // it sorts, caps the list, and emits a "(+N more)" remainder line.
177+ const perRepoSection = buildPerRepoRecapSection ( { windowDays : report . windowDays , repos : report . repos } ) ;
178+ const perRepoLines = perRepoSection . lines . map ( redactRecapLine ) ;
179+ const calibrationSection = buildCalibrationRecapSection ( { windowDays : report . windowDays , totals : report . totals } ) ;
180+ const gateOutcomesSection = buildGateOutcomesRecapSection ( { windowDays : report . windowDays , totals : report . totals } ) ;
174181 const lines = [
175182 "# Maintainer recap" ,
176183 "" ,
@@ -191,6 +198,14 @@ export function formatMaintainerRecap(report: RecapReport, options: { configDrif
191198 "" ,
192199 "## Per-repo" ,
193200 ...recapSectionLines ( perRepoLines , "_No repositories in this window._" ) ,
201+ "" ,
202+ // #8372: unconditional (not behind an options flag) -- both sections read only report.totals/windowDays,
203+ // which every RecapReport always carries, so there is nothing for a caller to opt into.
204+ `## ${ redactRecapLine ( calibrationSection . title ) } ` ,
205+ ...recapSectionLines ( calibrationSection . lines . map ( redactRecapLine ) , "_No calibration lines for this window._" ) ,
206+ "" ,
207+ `## ${ redactRecapLine ( gateOutcomesSection . title ) } ` ,
208+ ...recapSectionLines ( gateOutcomesSection . lines . map ( redactRecapLine ) , "_No gate-outcome lines for this window._" ) ,
194209 // #8214: optional config-drift section (maintainer-recap-drift.ts) — appended only when the caller has a
195210 // sentinel projection to render, so every existing digest stays byte-identical until the sentinel wires in.
196211 ...( options . configDrift
@@ -257,6 +272,11 @@ export async function runMaintainerRecap(
257272 report ?: RecapReport ;
258273 /** When explicitly false, short-circuits before build/format/delivery. Default: run. */
259274 enabled ?: boolean ;
275+ /** #8372: forwarded to {@link formatMaintainerRecap} so a caller holding a drift projection can have it
276+ * rendered. Deliberately NOT sourced here -- reading the knob-loosening sentinel state is its own
277+ * data-sourcing concern; this is only the plumbing, so the section stays absent until a caller passes it
278+ * and every existing digest is unaffected. */
279+ configDrift ?: DriftRecapSection ;
260280 } = { } ,
261281) : Promise < RunMaintainerRecapResult > {
262282 if ( options . enabled === false ) return { skipped : true , reason : "disabled" } ;
@@ -271,7 +291,13 @@ export async function runMaintainerRecap(
271291 // #8229 stage 1: the routing-shadow section reads the window's recorded decisions straight from the
272292 // audit trail — fail-safe to an absent section (the recap must never break on a read blip).
273293 const routingShadow = await loadRoutingRecapSection ( env , report . windowDays , options . generatedAt ?? nowIso ( ) ) ;
274- const formatted = formatMaintainerRecap ( report , routingShadow ? { routingShadow } : { } ) ;
294+ // Built up key-by-key rather than passed as a conditional-spread literal: exactOptionalPropertyTypes
295+ // forbids handing either key an explicit `undefined`, and #8229's routingShadow and #8372's configDrift
296+ // are independent — each is present or absent on its own, so a single ternary can't express all four cases.
297+ const recapOptions : { routingShadow ?: { title : string ; lines : string [ ] } ; configDrift ?: DriftRecapSection } = { } ;
298+ if ( routingShadow ) recapOptions . routingShadow = routingShadow ;
299+ if ( options . configDrift ) recapOptions . configDrift = options . configDrift ;
300+ const formatted = formatMaintainerRecap ( report , recapOptions ) ;
275301 const [ discord , slack ] = await Promise . all ( [
276302 deliverRecapToDiscord ( env , report , formatted ) ,
277303 deliverRecapToSlack ( env , report , formatted ) ,
0 commit comments