This website requires JavaScript.
Explore
Help
Sign In
starred
/
warmbly
Watch
1
Star
0
Fork
0
mirror of
https://github.com/warmbly/warmbly.git
synced
2026-09-12 00:05:09 +00:00
Code
Issues
Packages
Projects
Releases
Wiki
Activity
Files
da4b89da0ba641cd502be9e896dd4bece5eced24
warmbly
/
internal
/
scheduler
T
History
Matthew Meszaros
68b5d8f358
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
..
behavior_test.go
feat: give each mailbox a human sending persona (randomized daily and hourly caps, send spacing, work start/end, lunch break and working weekdays, rolled once per local day in the mailbox's own timezone and applied across the campaign, warmup and smart-send schedulers), add campaign auto-pause guardrails that stop a campaign when its bounce, complaint or reply rate leaves the configured band, make mailbox rotation actually rotate for tag-resolved and all-mailbox campaigns, stop every scheduler from ever returning a slot in the past, and correct the mailbox min-gap field that stored seconds while labelling them minutes
2026-08-13 16:51:29 +02:00
behavior.go
feat: give each mailbox a human sending persona (randomized daily and hourly caps, send spacing, work start/end, lunch break and working weekdays, rolled once per local day in the mailbox's own timezone and applied across the campaign, warmup and smart-send schedulers), add campaign auto-pause guardrails that stop a campaign when its bounce, complaint or reply rate leaves the configured band, make mailbox rotation actually rotate for tag-resolved and all-mailbox campaigns, stop every scheduler from ever returning a slot in the past, and correct the mailbox min-gap field that stored seconds while labelling them minutes
2026-08-13 16:51:29 +02:00
campaign_scheduler.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
campaign_sender.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
cold_ramp.go
feat: ease a graduating mailbox into cold volume (
#231
)
2026-08-28 10:47:03 -07:00
contact_preview_live_test.go
feat: add first-touch contact source attribution (migration 000106: contacts.source with a CHECK, source_detail, first_seen_at, existing rows stamped unknown) stamped at every creation site (dashboard manual/campaign, file import, Google Sheets sync, API key, AI assistant), write contact_created / campaign_added / campaign_removed / category_added / category_removed lifecycle events into contact_activities inside the same transactions as the links and merge them into the contact timeline, refactor FindNextRoutedPair's routing into a campaignRouter shared with a per-contact RouteContact and split CalculateNextCampaignTime into campaignSenders + placeCampaignSend so a read-only PreviewContactSend derives a contact's next step through the scheduler's own constraints, expose it as GET /contacts/:id/campaigns behind the contact service, render a per-campaign state panel, Campaigns/Lifecycle filter chips and expandable event rows in the Activity tab plus a Source section on Overview, cover it with TestLive* for the timeline events and the next-action preview (step wait, sending window, paused), and document it in the contacts, campaigns and API reference docs (issue
#255
)
2026-08-29 03:28:16 -07:00
contact_preview_test.go
feat: address Greptile on PR
#260
by only linking meeting join_url values whose scheme is http or https in the contact timeline (both the meta Join link and the expanded detail), projecting the campaign's daily ramp advance in memory before the read-only next-send preview so a preview on a new UTC day budgets with the level the next scheduler pass will persist (with a unit test), and trimming the campaign-state model, preview and service comments to one-line invariants
2026-08-29 03:51:24 -07:00
contact_preview.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
daily_budget_live_test.go
feat: pick a closed-hours mailbox that stays closed for the next ten minutes so the scheduling pass cannot run after it opens, and assert the mixed-pool deferral lands on the reopening from both sides instead of accepting any earlier time
2026-09-04 02:59:00 -07:00
domain_auth_test.go
feat: enforce the persisted SPF/DKIM/DMARC state as a real cold-send and warmup gate behind a 72h grace clock and an operator toggle, after first fixing the DMARC organizational-domain fallback in dnsauth so a dedicated sending subdomain covered by its parent's record stops reading as unauthenticated, adding auth_state to the four mailbox loaders that never selected it (which would have made the gate dead code), stamping auth_failing_since on entry to failing so a resolver hiccup can never stop a campaign, notifying the org on that transition, and reporting an all-gated pool as ErrDomainAuthFailing instead of a message about sending windows (
#160
)
2026-08-22 09:37:26 -07:00
email_scheduler.go
feat: give each mailbox a human sending persona (randomized daily and hourly caps, send spacing, work start/end, lunch break and working weekdays, rolled once per local day in the mailbox's own timezone and applied across the campaign, warmup and smart-send schedulers), add campaign auto-pause guardrails that stop a campaign when its bounce, complaint or reply rate leaves the configured band, make mailbox rotation actually rotate for tag-resolved and all-mailbox campaigns, stop every scheduler from ever returning a slot in the past, and correct the mailbox min-gap field that stored seconds while labelling them minutes
2026-08-13 16:51:29 +02:00
entry_delay_live_test.go
feat: carry the unexcluded routing pass's own re-check time back when the daily new-lead cap is spent, so a campaign whose only remaining leads are still inside the entry delay defers to their due time instead of reporting itself completed, with a live test that fails without it
2026-09-08 05:13:13 -07:00
errors.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
helpers.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
humanize_test.go
test: cover the gmail/graph message mapping (read-state inversion, spam/category labels, unpadded base64url bodies, warmup and classification header surfacing), the imap header-flag parser, the graph MIME builder, and the human-behavior timing (sub-minute randomisation, daily volume factor, night-deferred and heavy-tailed engagement)
2026-07-04 11:45:01 +02:00
live_integration_test.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
no_eligible_mailbox_test.go
feat: stop a freshly connected mailbox being silently excluded from every campaign send, by making an unset mailbox timezone representable as the empty string the campaign scheduler already checks for, since email_accounts.timezone defaulted to 'UTC' while campaigns.timezone defaults to 'Europe/London' and nothing in the OAuth or SMTP onboarding paths ever set either, so a brand new mailbox looked deliberately placed in UTC, was compared against the differing campaign zone and dropped by the hardcoded 8am-8pm business-hours gate whenever the current UTC hour fell outside it, emptying the candidate pool and failing the campaign start, adding a migration that changes the column default and converts existing 'UTC' rows because until now no API field, dashboard control or onboarding path could set that column at all so every such row is the old default rather than a choice, adding the missing Timezone field to UpdateEmail with IANA validation so the setting the sending-behaviour UI already tells people to change is finally reachable and an unloadable zone is rejected instead of being silently coerced to UTC by the scheduler, and replacing the misleading 'no active email accounts found for campaign's email tags' response for a pool that exists but is entirely gated out with a distinct message naming the real cause, via an ErrNoEligibleMailbox that wraps ErrNoEmailAccounts so the three callers that pause a campaign on it are unaffected (
#126
)
2026-08-16 07:46:37 +02:00
pacing_test.go
feat: bind a campaign lead to the mailbox that sends its first email so every follow-up leaves from the same address, holding a lead back while its mailbox is merely out of budget or outside its hours and moving it to another mailbox only when that one can no longer send for the campaign at all
2026-09-09 08:51:00 -07:00
parse_time_test.go
feat: make send-time optimization actually schedule sends (
#226
)
2026-08-28 09:46:39 -07:00
send_time_test.go
feat: make send-time optimization actually schedule sends (
#226
)
2026-08-28 09:46:39 -07:00
send_time.go
feat: make send-time optimization actually schedule sends (
#226
)
2026-08-28 09:46:39 -07:00
service.go
feat: give a cold mailbox a rotation lifecycle so a tired one can rest (
#237
)
2026-08-28 12:08:52 -07:00
tenancy_live_test.go
feat: scope campaign sender resolution to the campaign's organization instead of its owner so a multi-org user can no longer send organization A's campaign from an organization B mailbox: GetByTags/GetAllActiveInScope/GetByCampaignSenders now take a repository.AccountScope keyed on organization_id where a scope with no organization resolves to no mailboxes rather than widening to the owner (tags carry no organization of their own, so one user's tag legitimately spans workspaces and the predicate is the only thing holding the boundary), the campaign scheduler and the preflight tracking-domain check build that scope from campaign.OrganizationID, unibox compose scores only the current workspace's mailboxes, the 'all' lanes of AccountHasActiveCampaign/CountActiveCampaignsForAccount join ea.organization_id = c.organization_id instead of ea.user_id = c.user_id, dead and broken PauseAllByUserID (which wrote the reason string into status and had no callers) is removed, the campaigns/unibox guides now state that senders resolve inside the campaign's workspace, and TestLiveOrglessCampaignDoesNotSendToSuppressedRecipient is updated because an orgless campaign now finds no senders before routing is consulted while the send gate it covers still refuses; live-tested in TestLiveSenderResolutionStaysInsideTheCampaignOrg, TestLiveSenderSchedulerNeverPicksAnotherOrgMailbox, TestLiveSenderSchedulerPicksTheCampaignOrgMailbox, TestLiveSenderScopeWithoutAnOrganizationReachesNothing and TestLiveActiveCampaignLookupIsOrgScoped
2026-08-24 09:20:41 -07:00
warmup_pool_type_test.go
feat: keep a restricted or suspended workspace out of the premium warmup pool by checking the organization's risk posture before the stored warmup_pool_type in resolveWarmupPoolType (tasks and email services), which is never empty so the restricted branch below it was dead code, make the scheduler's recipient-capacity count risk-aware the same way, wire the org risk repository into the email service, and refuse the premium membership move in UpdateEmailAccountWarmupPoolType while the owning organization is restricted so a worker rebalance cannot readmit a risky tenant
2026-08-28 21:26:57 -07:00
warmup_scheduler_test.go
feat: close the warmup ramp loop with an early-signal hold (
#227
)
2026-08-28 10:06:19 -07:00
warmup_scheduler.go
feat: address the Greptile review on the pool link: require https for the cloud URL (loopback exempt for local development) since the instance token and mailbox passwords travel on it, remove the cloud copy when the local enrollment row cannot be written so a mailbox never warms in both places, delete the local enrollment row before the cloud one and restore it if the cloud call fails so a mailbox is never left with no warmup anywhere, and trim the new multi-line comments to the one-line style
2026-08-29 07:17:10 -07:00