You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The maintainer recap digest (RecapReport, #1963) has four dedicated "content slice" section builders,
each shipped in its own file with a doc comment explicitly describing itself as a slice of the composed
digest:
All three fields these first three sections need (totals.merged/closed/reversals/blocked/ gateFalsePositives/gateOverrides, and repos) are already present on every RecapReport produced by buildMaintainerRecap in src/services/maintainer-recap.ts — no new data sourcing is required to wire
them in.
Despite this, formatMaintainerRecap (src/services/maintainer-recap.ts, the function that actually
renders a RecapReport into the digest body posted to Discord/Slack) never calls any of them:
buildCalibrationRecapSection and buildGateOutcomesRecapSection are never called anywhere in src/**
outside their own unit tests (test/unit/maintainer-recap-calibration.test.ts, test/unit/maintainer-recap-gate-outcomes.test.ts) — confirmed by repo-wide grep. Fully implemented,
fully tested, never composed into the real output.
buildPerRepoRecapSection is also never called in production; formatMaintainerRecap instead inlines
its own simpler, duplicate per-repo line format (the perRepoLines map at src/services/maintainer-recap.ts around line 168) that has no cap, no "(+N more)" remainder handling,
and no sort — a second, drifted implementation of the same section sitting next to the unused dedicated
one.
buildDriftRecapSection's output IS renderable — formatMaintainerRecap accepts an optional options.configDrift: DriftRecapSection parameter and appends it when present (added for calibration: config-drift section in the maintainer recap #8214) — but runMaintainerRecap (the only production caller of formatMaintainerRecap, same file, around line 241)
calls formatMaintainerRecap(report) with no options argument at all. The config-drift section is
therefore never rendered in a real, delivered digest either; it is only exercised via test/unit/maintainer-recap-format.test.ts calling formatMaintainerRecap directly with a hand-built configDrift value.
This repo already has a recorded instance of exactly this bug shape (closed issue #6636:
"resolveDispositionReason is fully implemented and tested but never called from production code") — this
issue is the same pattern recurring across four sibling section builders in one feature area.
Net effect: a maintainer running the live recap digest today only ever sees the ad-hoc "Summary / Totals /
(duplicate, uncapped) Per-repo" text formatMaintainerRecap hand-rolls — never the calibration section, the
gate-outcomes section, the properly-capped per-repo section, or the config-drift section, even though all
four were separately scoped, built, and merged specifically to be part of this digest.
Requirements
In formatMaintainerRecap (src/services/maintainer-recap.ts), replace the inline perRepoLines
block with a call to buildPerRepoRecapSection({ windowDays: report.windowDays, repos: report.repos })
(imported from ./maintainer-recap-per-repo), rendering its .lines the same way the existing ## Per-repo section already renders (through recapSectionLines/redactRecapLine). Preserve the ## Per-repo header.
Add a ## Calibration section to formatMaintainerRecap's output, built by calling buildCalibrationRecapSection({ windowDays: report.windowDays, totals: report.totals }) (imported from ./maintainer-recap-calibration) and rendering its .lines through the same recapSectionLines/ redactRecapLine path as every other section. This section is unconditional (not behind an optional options flag) since its inputs are always present on any RecapReport.
Add a ## Gate outcomes section the same way, via buildGateOutcomesRecapSection({ windowDays: report.windowDays, totals: report.totals }) (imported from ./maintainer-recap-gate-outcomes).
Unconditional, same reasoning as point 2.
In runMaintainerRecap, pass a configDrift option through to formatMaintainerRecap whenever the
caller supplied one. Add an optional configDrift?: DriftRecapSection field to runMaintainerRecap's own options parameter and forward it as formatMaintainerRecap(report, { configDrift: options.configDrift }). Do NOT attempt to source live
drift data inside runMaintainerRecap itself (that requires querying the knob-loosening sentinel state
in src/services/loosening-knobs.ts, which is a separate, larger concern with its own data-sourcing
design) — this issue only needs the plumbing to exist so a caller that already has a DriftRecapSection
(or a future caller that builds one) can pass it through and have it actually render. Leave a code
comment noting that src/review/maintainer-recap-wire.ts (the real cron caller) does not yet supply one.
Keep the section ORDER as: Summary, Totals, Per-repo, Calibration, Gate outcomes, Config drift
(optional, last) — Totals stays as-is (it is not one of the four dormant section builders and is
already correct).
Do not change buildMaintainerRecap's RecapReport shape, and do not touch buildTopContributorsRecapSection (src/services/maintainer-recap-top-contributors.ts) — that section
needs a contributors: TopContributor[] list that is NOT present on RecapReport today, so wiring it in
requires a separate data-sourcing decision outside this issue's scope.
Deliverables
formatMaintainerRecap renders ## Per-repo via buildPerRepoRecapSection instead of its own inline
duplicate.
formatMaintainerRecap renders a new, unconditional ## Calibration section via buildCalibrationRecapSection.
formatMaintainerRecap renders a new, unconditional ## Gate outcomes section via buildGateOutcomesRecapSection.
runMaintainerRecap accepts and forwards an optional configDrift option to formatMaintainerRecap.
Test Coverage Requirements
99%+ Codecov patch coverage, branch-counted, on every changed line in src/**. At minimum:
formatMaintainerRecap: a report with populated repos/totals renders all three new/replaced sections
with the same content the standalone section-builder unit tests already assert (cross-check against test/unit/maintainer-recap-calibration.test.ts / -gate-outcomes.test.ts / -per-repo.test.ts fixtures
so the wired output matches the already-tested builder output exactly).
An empty RecapReport (zero repos, zero totals) still renders all sections with their documented
zero/empty-case text (each builder already has its own zero-case branch — assert the composed output
reaches it).
runMaintainerRecap forwards a supplied configDrift through to the rendered output, and omits the ## Config drift section entirely when none is supplied (existing behavior, must not regress).
Update test/unit/maintainer-recap.test.ts and test/unit/maintainer-recap-format.test.ts for the new
section content; do not delete existing assertions about Summary/Totals.
Expected Outcome
The live maintainer recap digest delivered to Discord/Slack actually contains the calibration,
gate-outcomes, and properly-capped per-repo sections that were separately scoped and shipped for exactly
this purpose (#2241, #2242, #2243), instead of silently omitting them in favor of ad-hoc inline text — and
a caller that supplies config-drift data can have it rendered without needing to bypass runMaintainerRecap.
Links & Resources
src/services/maintainer-recap.ts — formatMaintainerRecap, runMaintainerRecap (both to change).
src/services/maintainer-recap-calibration.ts, -gate-outcomes.ts, -per-repo.ts, -drift.ts — the
four dormant/partially-wired section builders.
Context
The maintainer recap digest (
RecapReport, #1963) has four dedicated "content slice" section builders,each shipped in its own file with a doc comment explicitly describing itself as a slice of the composed
digest:
buildCalibrationRecapSection—src/services/maintainer-recap-calibration.ts(feat(notifications): calibration / reversal-rate section for the maintainer recap #2243): reversal count /reversal-rate section, needs only
report.windowDays+report.totals.{merged,closed,reversals}.buildGateOutcomesRecapSection—src/services/maintainer-recap-gate-outcomes.ts(feat(notifications): gate-outcomes section for the maintainer recap (blocked / overridden / false-positive) #2242): blocked /overridden / false-positive section, needs only
report.windowDays+report.totals.{blocked,gateFalsePositives,gateOverrides}.buildPerRepoRecapSection—src/services/maintainer-recap-per-repo.ts(feat(notifications): per-repo summary section for the maintainer recap #2241): a capped, sortedper-repo breakdown with a "(+N more)" remainder line, needs only
report.windowDays+report.repos.buildDriftRecapSection—src/services/maintainer-recap-drift.ts(calibration: config-drift section in the maintainer recap #8214): a config-drift section.All three fields these first three sections need (
totals.merged/closed/reversals/blocked/ gateFalsePositives/gateOverrides, andrepos) are already present on everyRecapReportproduced bybuildMaintainerRecapinsrc/services/maintainer-recap.ts— no new data sourcing is required to wirethem in.
Despite this,
formatMaintainerRecap(src/services/maintainer-recap.ts, the function that actuallyrenders a
RecapReportinto the digest body posted to Discord/Slack) never calls any of them:buildCalibrationRecapSectionandbuildGateOutcomesRecapSectionare never called anywhere insrc/**outside their own unit tests (
test/unit/maintainer-recap-calibration.test.ts,test/unit/maintainer-recap-gate-outcomes.test.ts) — confirmed by repo-wide grep. Fully implemented,fully tested, never composed into the real output.
buildPerRepoRecapSectionis also never called in production;formatMaintainerRecapinstead inlinesits own simpler, duplicate per-repo line format (the
perRepoLinesmap atsrc/services/maintainer-recap.tsaround line 168) that has no cap, no "(+N more)" remainder handling,and no sort — a second, drifted implementation of the same section sitting next to the unused dedicated
one.
buildDriftRecapSection's output IS renderable —formatMaintainerRecapaccepts an optionaloptions.configDrift: DriftRecapSectionparameter and appends it when present (added for calibration: config-drift section in the maintainer recap #8214) — butrunMaintainerRecap(the only production caller offormatMaintainerRecap, same file, around line 241)calls
formatMaintainerRecap(report)with nooptionsargument at all. The config-drift section istherefore never rendered in a real, delivered digest either; it is only exercised via
test/unit/maintainer-recap-format.test.tscallingformatMaintainerRecapdirectly with a hand-builtconfigDriftvalue.This repo already has a recorded instance of exactly this bug shape (closed issue #6636:
"resolveDispositionReason is fully implemented and tested but never called from production code") — this
issue is the same pattern recurring across four sibling section builders in one feature area.
Net effect: a maintainer running the live recap digest today only ever sees the ad-hoc "Summary / Totals /
(duplicate, uncapped) Per-repo" text
formatMaintainerRecaphand-rolls — never the calibration section, thegate-outcomes section, the properly-capped per-repo section, or the config-drift section, even though all
four were separately scoped, built, and merged specifically to be part of this digest.
Requirements
formatMaintainerRecap(src/services/maintainer-recap.ts), replace the inlineperRepoLinesblock with a call to
buildPerRepoRecapSection({ windowDays: report.windowDays, repos: report.repos })(imported from
./maintainer-recap-per-repo), rendering its.linesthe same way the existing## Per-reposection already renders (throughrecapSectionLines/redactRecapLine). Preserve the## Per-repoheader.## Calibrationsection toformatMaintainerRecap's output, built by callingbuildCalibrationRecapSection({ windowDays: report.windowDays, totals: report.totals })(imported from./maintainer-recap-calibration) and rendering its.linesthrough the samerecapSectionLines/redactRecapLinepath as every other section. This section is unconditional (not behind an optionaloptionsflag) since its inputs are always present on anyRecapReport.## Gate outcomessection the same way, viabuildGateOutcomesRecapSection({ windowDays: report.windowDays, totals: report.totals })(imported from./maintainer-recap-gate-outcomes).Unconditional, same reasoning as point 2.
runMaintainerRecap, pass aconfigDriftoption through toformatMaintainerRecapwhenever thecaller supplied one. Add an optional
configDrift?: DriftRecapSectionfield torunMaintainerRecap's ownoptionsparameter and forward it asformatMaintainerRecap(report, { configDrift: options.configDrift }). Do NOT attempt to source livedrift data inside
runMaintainerRecapitself (that requires querying the knob-loosening sentinel statein
src/services/loosening-knobs.ts, which is a separate, larger concern with its own data-sourcingdesign) — this issue only needs the plumbing to exist so a caller that already has a
DriftRecapSection(or a future caller that builds one) can pass it through and have it actually render. Leave a code
comment noting that
src/review/maintainer-recap-wire.ts(the real cron caller) does not yet supply one.(optional, last) — Totals stays as-is (it is not one of the four dormant section builders and is
already correct).
buildMaintainerRecap'sRecapReportshape, and do not touchbuildTopContributorsRecapSection(src/services/maintainer-recap-top-contributors.ts) — that sectionneeds a
contributors: TopContributor[]list that is NOT present onRecapReporttoday, so wiring it inrequires a separate data-sourcing decision outside this issue's scope.
Deliverables
formatMaintainerRecaprenders## Per-repoviabuildPerRepoRecapSectioninstead of its own inlineduplicate.
formatMaintainerRecaprenders a new, unconditional## Calibrationsection viabuildCalibrationRecapSection.formatMaintainerRecaprenders a new, unconditional## Gate outcomessection viabuildGateOutcomesRecapSection.runMaintainerRecapaccepts and forwards an optionalconfigDriftoption toformatMaintainerRecap.Test Coverage Requirements
99%+ Codecov patch coverage, branch-counted, on every changed line in
src/**. At minimum:formatMaintainerRecap: a report with populatedrepos/totalsrenders all three new/replaced sectionswith the same content the standalone section-builder unit tests already assert (cross-check against
test/unit/maintainer-recap-calibration.test.ts/-gate-outcomes.test.ts/-per-repo.test.tsfixturesso the wired output matches the already-tested builder output exactly).
RecapReport(zero repos, zero totals) still renders all sections with their documentedzero/empty-case text (each builder already has its own zero-case branch — assert the composed output
reaches it).
runMaintainerRecapforwards a suppliedconfigDriftthrough to the rendered output, and omits the## Config driftsection entirely when none is supplied (existing behavior, must not regress).test/unit/maintainer-recap.test.tsandtest/unit/maintainer-recap-format.test.tsfor the newsection content; do not delete existing assertions about Summary/Totals.
Expected Outcome
The live maintainer recap digest delivered to Discord/Slack actually contains the calibration,
gate-outcomes, and properly-capped per-repo sections that were separately scoped and shipped for exactly
this purpose (#2241, #2242, #2243), instead of silently omitting them in favor of ad-hoc inline text — and
a caller that supplies config-drift data can have it rendered without needing to bypass
runMaintainerRecap.Links & Resources
src/services/maintainer-recap.ts—formatMaintainerRecap,runMaintainerRecap(both to change).src/services/maintainer-recap-calibration.ts,-gate-outcomes.ts,-per-repo.ts,-drift.ts— thefour dormant/partially-wired section builders.
production code" bug shape in this repo.
test/unit/maintainer-recap*.test.ts— existing coverage to extend.