feat: skip weekend digests and roll them into Monday - #54
Conversation
Community feedback: Saturday and Sunday digests go unread, and weekend
activity is too thin to warrant its own post.
Cloud Scheduler still fires daily; the application now no-ops on Sat/Sun
(UTC) and extends Monday's lookback by 48h so the two skipped runs are
covered with no gap. Keeping the rule in code rather than in the cron
expression is what makes the Monday extension possible at all —
Scheduler cannot vary DIGEST_WINDOW_HOURS per day.
- getRunSchedule() (src/utils/time.ts) is the single place the weekday
rule is stated: returns {skip, extraHours} from a UTC weekday.
- SKIP_WEEKEND (default true) gates the whole policy. Setting it false
restores plain daily behaviour: weekend runs happen AND Monday loses
its extension, so the two halves can never contradict each other.
- extraHours is threaded through Source.fetchMessages as a separate
argument rather than pre-added, so it stacks on top of
DISCOURSE_LOOKBACK_HOURS instead of being swallowed by that override.
- The digest header now describes the window actually fetched. It
previously rendered a calendar day against a rolling 13:00-to-13:00
window, misreporting by ~13h; a 72h Monday digest labelled with a
single date would have made that visible.
- truncateMessages warns when it drops messages. It discards the oldest,
which on a Monday roll-up is Friday's content.
- The runbook's "digest missing" alert would have false-alarmed every
weekend; §6/§7 now carry the carve-out, and §9 gains a missed-Monday
recovery recipe (three days of content, not one).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #54 +/- ##
==========================================
+ Coverage 80.95% 82.11% +1.16%
==========================================
Files 12 12
Lines 483 492 +9
Branches 132 137 +5
==========================================
+ Hits 391 404 +13
+ Misses 31 28 -3
+ Partials 61 60 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Pull request overview
Implements weekday-only digest runs by skipping Sat/Sun (UTC) in-app and rolling weekend activity into Monday via an extra 48h lookback, while keeping Cloud Scheduler firing daily.
Changes:
- Added
getRunSchedule()+SKIP_WEEKENDconfig to skip weekend runs and extend Monday’s lookback byWEEKEND_EXTRA_HOURS(48). - Updated the pipeline, sources, and Slack header/window rendering to support a rolling time window and Monday date ranges.
- Added/updated unit tests and operational documentation for the new weekday-only behavior.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| test/unit/time.test.ts | New tests covering getRunSchedule() behavior across days and UTC boundary cases. |
| test/unit/discourse_weekend_extension.test.ts | New tests ensuring Discourse lookback overrides stack with the weekend extension. |
| test/unit/digest_pipeline.test.ts | Adds pipeline-level tests for weekend no-op and Monday extension propagation + titles. |
| test/unit/config.test.ts | Extends config tests to cover SKIP_WEEKEND defaulting and parsing. |
| src/utils/time.ts | Introduces WEEKEND_EXTRA_HOURS and getRunSchedule(). |
| src/utils/format.ts | Updates Slack “Time window” rendering to show actual fetched rolling timestamps. |
| src/services/llm/AiSdkProcessor.ts | Adds a warning when truncation drops (oldest) messages. |
| src/services/discourse/DiscourseSource.ts | Adds extraHours support and stacks it with DISCOURSE_LOOKBACK_HOURS override. |
| src/services/discord/DiscordSource.ts | Adds extraHours support for extended lookback on Mondays. |
| src/DigestPipeline.ts | Applies getRunSchedule() to skip weekends and extend Monday; updates context date titles and source calls. |
| src/core/interfaces.ts | Extends Source.fetchMessages() contract to accept optional extraHours. |
| src/config/index.ts | Adds SKIP_WEEKEND config with correct empty-string defaulting behavior. |
| README.md | Updates product description to reflect weekday-only digests. |
| docs/production-runbook.md | Updates ops/runbook for weekend behavior, alert suppression, and missed-Monday recovery steps. |
| AGENTS.md | Documents the weekday-only digest policy and relevant code entry points. |
| .github/workflows/daily-digest.yml | Passes SKIP_WEEKEND variable into workflow environment. |
| .env.example | Documents SKIP_WEEKEND in the example environment file. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Addresses review feedback on #54. DISCOURSE_LOOKBACK_HOURS let the forum fetch a different window than the one advertised in the digest header, so a larger override could surface messages older than the header claimed to cover. Rather than teach the header to compute max() across every source's private window, remove the divergence: all sources now use DIGEST_WINDOW_HOURS (plus the Monday weekend catch-up), so the advertised window and the fetched window are the same number by construction. With no per-source override left, the separate `extraHours` argument on Source.fetchMessages had no remaining purpose — it existed only so the weekend catch-up could stack on top of that override. The interface reverts to fetchMessages(windowHours), and DigestPipeline passes the total it already computed for the header. Also corrects a wrong comment in getRunSchedule: 13:00 UTC is not the same calendar day everywhere (UTC+13/+14 are already on the next day). The reason to use getUTCDay() is to avoid depending on the host's local timezone, not that timezones happen to agree. Note for deploy: if DISCOURSE_LOOKBACK_HOURS is set as a repo Variable it becomes inert rather than failing — the Zod schema ignores unknown keys. It can be deleted at any time. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Why
Community feedback: Saturday and Sunday digests go unread, and weekend activity is too thin to warrant its own post. This drops to five digests a week with no content loss — the weekend rolls into Monday.
How
Cloud Scheduler keeps firing daily (
0 13 * * *UTC, unchanged). The application no-ops on Sat/Sun and extends Monday's lookback by 48h, covering Fri 13:00 → Mon 13:00 with no gap.Keeping the rule in code rather than the cron expression is what makes the Monday extension possible at all: Cloud Scheduler cannot vary
DIGEST_WINDOW_HOURSper day — it arrives as a repo Variable identical on every run. Cost is two Actions runs/week that exit in ~30s before contacting any API.Weekend (UTC) — skipping digest run, exits 0. No source, LLM, or Slack traffic.fetchMessages(24, 48)→ 72h window, header shows a date rangefetchMessages(24, 0)→ unchangedNotable decisions
getRunSchedule()(src/utils/time.ts) is the single place the weekday rule is stated — a pure, date-injected seam returning{skip, extraHours}.SKIP_WEEKEND(defaulttrue) gates the whole policy. Setting itfalserestores plain daily behaviour: weekend runs happen and Monday loses its extension, so the two halves can never contradict each other.DISCOURSE_LOOKBACK_HOURSis removed. It let the forum fetch a different window than the digest header advertised — a pre-existing divergence that the weekend extension would have widened. All sources now takeDIGEST_WINDOW_HOURSplus the Monday catch-up, so the advertised window and the fetched window are the same number by construction and cannot drift as sources are added.Source.fetchMessages(windowHours)keeps its original single-argument shape as a result.buildDigestBlocks' parameter shape is unchanged, so no existingformat.test.tscall sites needed edits.truncateMessagesnow warns when it drops messages. It discards the oldest, which on a Monday roll-up is Friday's content. Logging rather than raisingMAX_INPUT_CHARS_PER_GROUPso the limit can be retuned from evidence.Ops
The runbook's "digest missing" alert would have false-alarmed every weekend. §6/§7 now carry the carve-out (suppress the no-Slack-post half on Sat/Sun; the no-successful-run half still applies all seven days), and §9 gains a missed-Monday recovery recipe — that failure drops three days of content, not one.
No Cloud Scheduler change and no new repo Variable are required to deploy: an unset
${{ vars.SKIP_WEEKEND }}renders as"", whichtoBoolmaps toundefined, so the.default(true)inside the preprocess fires. Set the Variable only to turn the policy off.Also fixes pre-existing runbook drift: it claimed the workflow keeps a
scheduleblock as backup, which it does not.DISCOURSE_LOOKBACK_HOURSis removed. If it is set as a repo Variable it becomes inert rather than failing — the Zod schema ignores unknown keys — so it can be deleted whenever convenient. Forum lookback now followsDIGEST_WINDOW_HOURS.Testing
npm run buildclean; 140 tests pass across 17 files.test/unit/time.test.ts(new) — all 7 UTC weekdays × both flag values, plus explicit UTC-vs-local boundary cases.test/unit/discourse_weekend_extension.test.ts(new) — Monday's 72h window reaches forum content a 24h window misses, with boundary cases either side.digest_pipeline.test.ts— Sat/Sun make no calls; Monday passes72and titles the range correctly.config.test.ts—SKIP_WEEKENDdefault holds when unset and when empty-string (the GitHub Actions case).Verified end-to-end against the compiled pipeline driven at fixed clocks, confirming the rendered Slack header:
🤖 Generated with Claude Code