feat(notifications): idempotent per-period recap claim + generation audit trail - #4470
Conversation
…udit trail Adds a per-UTC-date claim (migration 0129, mirroring claimRegateFanoutSlot's atomic conditional-UPDATE pattern) so a retried cron tick or redelivered queue message never re-scans repos or re-sends the maintainer recap digest for a period already sent. Also records a maintainer_recap_generated audit event once the report is built, giving operators a ledger trail independent of the per-channel delivery-outcome event.
…ip shape Resolves the merge conflict with the JSONbored#4457 e2e-test refactor (which introduced the dual-channel runMaintainerRecap/RunMaintainerRecapResult shape): ranRecap now accepts the union of that result type and the job-level MaintainerRecapJobSkipped ("already_sent_this_period") case.
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4470 +/- ##
=======================================
Coverage 94.02% 94.02%
=======================================
Files 420 420
Lines 37479 37491 +12
Branches 13692 13694 +2
=======================================
+ Hits 35240 35252 +12
Misses 1583 1583
Partials 656 656
🚀 New features to boost your workflow:
|
…coverage runMaintainerRecapJob never passes enabled:false to runMaintainerRecap (the enable/disable decision already happened via isRecapEnabled before this function is ever invoked), so the skipped:true branch is unreachable from this call site -- mirrors the codebase's existing v8-ignore-else convention for the same class of defensive narrowing.
|
Warning 🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨🟨 ⏸️ Gittensory review result - manual review recommendedReview updated: 2026-07-09 20:59:38 UTC
⏸️ Suggested Action - Manual Review
Nits — 1 non-blocking
Linked issue satisfactionPartially addressed Review context
Contributor next steps
Signal definitions
🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed 💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.
|
Summary
Resubmission of #4459 (closed due to a merge conflict with #4457, which landed while this PR was open and refactored
runMaintainerRecapJob's delivery path to the new dual-channelrunMaintainerRecap/RunMaintainerRecapResultshape). Same feature content, rebased onto currentmainand adapted to the new shape.claimMaintainerRecapPeriod(src/db/repositories.ts) performs an atomic conditionalUPDATEon theglobal_agent_controlssingleton, mirroringclaimRegateFanoutSlot's proven pattern (migration 0063). Fail-open on a driver error, matching that same philosophy.migrations/0129_maintainer_recap_claim.sql(ALTER TABLE global_agent_controls ADD COLUMN last_recap_period_key TEXT).runMaintainerRecapJobnow returnsMaintainerRecapJobSkipped | RunMaintainerRecapResult— a claimed-but-skipped period returns{ skipped: true, reason: "already_sent_this_period" }before any repo scan or delivery attempt (Discord + Slack, per test(notifications): maintainer recap end-to-end orchestration #4457's dual-channel orchestrator).isRecapEnabled— was already completed in feat(notifications): schedule the maintainer recap job on a configurable cadence #4418. This PR adds only the missing idempotency piece.maintainer_recap_generatedaudit event once the report is built (afterrunMaintainerRecapsucceeds), mirroringgenerateWeeklyValueReport's ownrecordAuditEventcall. Metadata:cadence,windowDays,repoCount,sectionCount,channelsAttempted: ["discord", "slack"](both channels are always attempted by the test(notifications): maintainer recap end-to-end orchestration #4457 orchestrator, each independently best-effort).deliverRecapToDiscord/deliverRecapToSlack's own existing audit events. This PR adds only the missing generation-level event.Scope
type(scope): short summaryConventional Commit format.runMaintainerRecapJoborchestrator, both "Part of Maintainer review recap digest (Discord / Slack) #1963") rather than splitting into two PRs that would each touch the exact same lines.CONTRIBUTING.mdand does not reintroduce GitHub Pages, VitePress,site/, orCNAME.Closes #2249,Closes #2251.Validation
git diff --checknpm run db:migrations:check— contiguous, next free 0130npm run db:schema-drift:check—global_agent_controlsstays raw-SQL-only (allowlisted)npm run typechecknpm run test:coveragelocally — 654 test files / 12,796 tests green; 100% branch coverage on every changed line, verified againstlcov.infonpm audit --audit-level=moderate— 0 vulnerabilitiesmain(post-test(notifications): maintainer recap end-to-end orchestration #4457, post-feat(miner-manage): add maintainer-gate verdict poller (read-only) #4464) with no conflicts after the initial adaptation to the new dual-channel result shapeTest coverage added/updated:
test/unit/db-parsers.test.ts:claimMaintainerRecapPeriod— first claim for a period wins, a retry for the same period loses, a different period wins again; fails open on a DB error.test/unit/maintainer-recap-wire.test.ts: a retried tick within the same UTC date is a no-op (no repo scan, no second Discord post); a tick on a different UTC date gets its own fresh claim and sends again; themaintainer_recap_generatedaudit event's full metadata shape; a present manifest cadence override is reflected in the audit metadata over the env value. Existing tests updated for test(notifications): maintainer recap end-to-end orchestration #4457'sdelivery.discord/delivery.slacksplit shape.Safety
UI Evidencesection. (N/A — no visible UI changes; backend-only.)UI Evidence
N/A — backend-only change (D1 migration + queue orchestrator), no UI surface touched.
Notes