Skip to content

Switched to time-based domain warmup#9

Open
tomerqodo wants to merge 2 commits intocoderabbit_full_base_switched_to_time-based_domain_warmup_pr9from
coderabbit_full_head_switched_to_time-based_domain_warmup_pr9
Open

Switched to time-based domain warmup#9
tomerqodo wants to merge 2 commits intocoderabbit_full_base_switched_to_time-based_domain_warmup_pr9from
coderabbit_full_head_switched_to_time-based_domain_warmup_pr9

Conversation

@tomerqodo
Copy link
Copy Markdown

@tomerqodo tomerqodo commented Jan 29, 2026

Benchmark PR from agentic-review-benchmarks#9

Summary by CodeRabbit

  • Improvements
    • Enhanced email domain warming with a time-based progression system that gradually increases sending limits over a configurable period.
    • Improved calculation of email sending limits during the warmup phase based on elapsed time rather than fixed scaling rules.
    • Made warmup configuration more flexible and customizable through service settings.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

The email warming service's domain warmup logic is refactored from a hard-coded scaling table to a time-based progression model. The new system ramps email volume from a configurable start value to an end value over a defined number of days, replacing the previous manual scaling structure and simplifying the calculation approach.

Changes

Cohort / File(s) Summary
Core Service Implementation
ghost/core/core/server/services/email-service/DomainWarmingService.ts
Introduced WarmupVolumeOptions type and DefaultWarmupOptions constant. Expanded constructor to accept LabsService and ConfigService dependencies. Replaced legacy WARMUP_SCALING_TABLE and related methods with day-based warmup progression using mathematical formula. Added #getDaysSinceFirstEmail helper to track warmup age. Updated getWarmupLimit to compute daily-based limits with Infinity cap after warmup period completes.
Integration Tests
ghost/core/test/integration/services/email-service/domain-warming.test.js
Updated test assertions to use time-based warmup formula instead of fixed multipliers. Replaced branching logic with unified time-based calculation across multiple test scenarios. Adjusted day labels and assertion messages to reflect day-based progression model.
Unit Tests
ghost/core/test/unit/server/services/email-service/domain-warming-service.test.ts
Introduced fake timer for deterministic time-based testing. Added daysAgo() helper to simulate email aging via timestamps. Replaced lastCount-based test scenarios with day-based progression model. Updated filter expectations to use csd_email_count constraints. Expanded edge case coverage to verify Infinity results after warmup period completion.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 Hop, hop! The warmup now flows smooth and true,
No more tables to count—just days to pursue,
From start to its peak, the formula's dance,
Warming up emails with temporal grace! ✉️

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Switched to time-based domain warmup' accurately and concisely summarizes the main change: replacing the previous hard-coded/last-count-based warming logic with a day-based time progression model across the DomainWarmingService and related tests.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch coderabbit_full_head_switched_to_time-based_domain_warmup_pr9

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@tomerqodo
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@tomerqodo
Copy link
Copy Markdown
Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 30, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@avior-qc
Copy link
Copy Markdown

avior-qc commented Feb 2, 2026

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@ghost/core/core/server/services/email-service/DomainWarmingService.ts`:
- Around line 83-97: In getWarmupLimit, the warmup completion check and rounding
are off: change the completion test from using ">" to ">=" when comparing day to
this.#warmupConfig.totalDays (so the ramp stops on the completion day), switch
Math.floor to Math.round when computing limit so intermediate expected values
aren’t undercut, and add missing semicolons to the function body; update
references in the calculations that use this.#warmupConfig.start,
this.#warmupConfig.end, and this.#warmupConfig.totalDays and keep the final
return as Math.min(emailCount, limit).
- Around line 64-76: The day-count calculation in `#getDaysSinceFirstEmail` uses
Math.ceil which increments the day on any positive difference; change it to use
Math.floor on (Date.now() - new Date(res.data[0].get('created_at') as
string).getTime()) / (1000 * 60 * 60 * 24) and clamp the result to a minimum of
0 (e.g., Math.max(0, Math.floor(...))) so emails sent later the same day do not
advance the warmup day.
🧹 Nitpick comments (1)
ghost/core/test/integration/services/email-service/domain-warming.test.js (1)

199-203: Consider deriving expected limits from a shared helper to avoid magic numbers.
Hardcoding 237/280/etc. can drift if warmup defaults change. You already compute expected limits later; extracting a reusable helper would keep all tests aligned.

Also applies to: 223-247

Comment on lines +64 to 76
async #getDaysSinceFirstEmail(): Promise<number> {
const res = await this.#emailModel.findPage({
filter: 'csd_email_count:-null',
order: 'created_at ASC',
limit: 1
});

if (!result.data.length) {
if (!res.data.length) {
return 0;
}

const count = result.data[0].get('csd_email_count');
return count || 0;
return Math.ceil((Date.now() - new Date(res.data[0].get('created_at') as string).getTime()) / (1000 * 60 * 60 * 24));
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Day calculation advances within the same day; use floor/clamp.
Using Math.ceil means any positive diff becomes day 1, so a second send minutes later bumps the warmup. That contradicts same‑day stability and can overshoot early.

🛠️ Proposed fix
-        return Math.ceil((Date.now() - new Date(res.data[0].get('created_at') as string).getTime()) / (1000 * 60 * 60 * 24));
+        const diffMs = Date.now() - new Date(res.data[0].get('created_at') as string).getTime();
+        const diffDays = Math.max(0, Math.floor(diffMs / (1000 * 60 * 60 * 24)));
+        return diffDays;
🤖 Prompt for AI Agents
In `@ghost/core/core/server/services/email-service/DomainWarmingService.ts` around
lines 64 - 76, The day-count calculation in `#getDaysSinceFirstEmail` uses
Math.ceil which increments the day on any positive difference; change it to use
Math.floor on (Date.now() - new Date(res.data[0].get('created_at') as
string).getTime()) / (1000 * 60 * 60 * 24) and clamp the result to a minimum of
0 (e.g., Math.max(0, Math.floor(...))) so emails sent later the same day do not
advance the warmup day.

Comment on lines +83 to +97
async getWarmupLimit(emailCount: number): Promise<number> {
const day = await this.#getDaysSinceFirstEmail()
if (day > this.#warmupConfig.totalDays) {
return Infinity
}

for (const threshold of WARMUP_SCALING_TABLE.thresholds.sort((a, b) => a.limit - b.limit)) {
if (lastCount <= threshold.limit) {
return Math.ceil(lastCount * threshold.scale);
}
}
const limit = Math.floor(
this.#warmupConfig.start *
Math.pow(
this.#warmupConfig.end / this.#warmupConfig.start,
day / (this.#warmupConfig.totalDays - 1)
)
)

// This should not be reached given the thresholds cover all cases up to highVolume.threshold
return Math.ceil(lastCount * WARMUP_SCALING_TABLE.highVolume.maxScale);
return Math.min(emailCount, limit)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Warmup completion and rounding are off by one (and lint).
day > totalDays keeps the ramp active on the completion day, and Math.floor undercuts the expected progression values. Also, ESLint flags missing semicolons here.

🛠️ Proposed fix
-        const day = await this.#getDaysSinceFirstEmail()
-        if (day > this.#warmupConfig.totalDays) {
-            return Infinity
+        const day = await this.#getDaysSinceFirstEmail();
+        if (day >= this.#warmupConfig.totalDays) {
+            return Infinity;
         }

-        const limit = Math.floor(
+        const limit = Math.round(
             this.#warmupConfig.start *
             Math.pow(
                 this.#warmupConfig.end / this.#warmupConfig.start,
                 day / (this.#warmupConfig.totalDays - 1)
             )
-        )
+        );

-        return Math.min(emailCount, limit)
+        return Math.min(emailCount, limit);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async getWarmupLimit(emailCount: number): Promise<number> {
const day = await this.#getDaysSinceFirstEmail()
if (day > this.#warmupConfig.totalDays) {
return Infinity
}
for (const threshold of WARMUP_SCALING_TABLE.thresholds.sort((a, b) => a.limit - b.limit)) {
if (lastCount <= threshold.limit) {
return Math.ceil(lastCount * threshold.scale);
}
}
const limit = Math.floor(
this.#warmupConfig.start *
Math.pow(
this.#warmupConfig.end / this.#warmupConfig.start,
day / (this.#warmupConfig.totalDays - 1)
)
)
// This should not be reached given the thresholds cover all cases up to highVolume.threshold
return Math.ceil(lastCount * WARMUP_SCALING_TABLE.highVolume.maxScale);
return Math.min(emailCount, limit)
async getWarmupLimit(emailCount: number): Promise<number> {
const day = await this.#getDaysSinceFirstEmail();
if (day >= this.#warmupConfig.totalDays) {
return Infinity;
}
const limit = Math.round(
this.#warmupConfig.start *
Math.pow(
this.#warmupConfig.end / this.#warmupConfig.start,
day / (this.#warmupConfig.totalDays - 1)
)
);
return Math.min(emailCount, limit);
🧰 Tools
🪛 ESLint

[error] 84-85: Missing semicolon.

(semi)


[error] 86-87: Missing semicolon.

(semi)


[error] 95-96: Missing semicolon.

(semi)

🤖 Prompt for AI Agents
In `@ghost/core/core/server/services/email-service/DomainWarmingService.ts` around
lines 83 - 97, In getWarmupLimit, the warmup completion check and rounding are
off: change the completion test from using ">" to ">=" when comparing day to
this.#warmupConfig.totalDays (so the ramp stops on the completion day), switch
Math.floor to Math.round when computing limit so intermediate expected values
aren’t undercut, and add missing semicolons to the function body; update
references in the calculations that use this.#warmupConfig.start,
this.#warmupConfig.end, and this.#warmupConfig.totalDays and keep the final
return as Math.min(emailCount, limit).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants