Commit Graph
2045 Commits
Author SHA1 Message Date
Matthew Meszaros 02c8d8ec27 feat: read the header's connection quality from the heartbeat roundtrip the socket provider already measures instead of an interval that compared itself against its own last tick and so reported a healthy socket as a slow one on almost every pass 2026-09-04 04:03:01 -07:00
Matthew Meszaros c894d4e355 Merge pull request #315 from warmbly/fix/issue-307-test-send-preview-from-name
Verify a campaign before launch: working test sends, a preview that renders the real thing, live sender name and signature spacing (#307)
2026-09-04 03:42:21 -07:00
Matthew Meszaros 483d6c2fe7 Merge remote-tracking branch 'origin/main' into fix/issue-307-test-send-preview-from-name 2026-09-04 03:34:35 -07:00
Matthew Meszaros ce85fd6188 feat: clear the rendered step preview alongside the contact and mailbox picks when the editor changes campaign, so the panel cannot keep showing the previous campaign's sender and attachments while the new render is in flight 2026-09-04 03:27:20 -07:00
Matthew Meszaros 43297882f1 feat: clear the step preview's contact and mailbox picks when the editor is pointed at another campaign or at none, and send the contact and account identifiers only alongside the campaign they belong to, so a preview can never carry a selection made for a different campaign 2026-09-04 03:19:54 -07:00
Matthew Meszaros 068b34751b Merge pull request #312 from warmbly/fix/campaign-daily-budget-deferrals
Campaign scheduler: count only real sends against the daily budget and defer at cap instead of pausing (#306)
2026-09-04 03:13:50 -07:00
Matthew Meszaros 915390f42a feat: match the closing body tag case-insensitively when placing the mailbox signature so a pasted document written with </BODY> still gets it inside the body, drop a step preview response whose request has been superseded so an out-of-order reply cannot repaint stale copy, and keep pulling mailbox pages in the preview sender picker while an enabled campaign sender is still unaccounted for 2026-09-04 03:11:53 -07:00
Matthew Meszaros 64e6e3a502 Merge remote-tracking branch 'origin/main' into fix/campaign-daily-budget-deferrals 2026-09-04 03:06:45 -07:00
Matthew Meszaros 14827e5123 Merge pull request #311 from warmbly/fix/contact-timeline-opaque-cursor
Contact timeline: opaque (at, source, id) cursor with a tie-break in every source
2026-09-04 03:05:49 -07:00
Matthew Meszaros de33b03891 feat: give the HTML mailbox signature its own block with a top margin instead of a hard double line break that stacked against the body's trailing margin and rendered as two to three blank lines in Apple Mail and Outlook, insert it inside the document when the body carries a closing body tag as the tracking pixel and opt-out footer already do, and say so in the mailboxes guide 2026-09-04 03:01:19 -07:00
Matthew Meszaros 2a19bf0519 Merge remote-tracking branch 'origin/main' into fix/campaign-daily-budget-deferrals 2026-09-04 02:59:01 -07:00
Matthew Meszaros 5c9d365163 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
Matthew Meszaros 997aa2c748 Merge remote-tracking branch 'origin/main' into fix/issue-307-test-send-preview-from-name 2026-09-04 02:58:40 -07:00
Matthew Meszaros aa55b31422 feat: give the campaign step preview a contact and mailbox picker so it renders for a real lead with that sender's signature, the opt-out footer and the attachment list, add a Send test action that mails the saved step through the chosen mailbox, and document both in the sequences guide 2026-09-04 02:58:36 -07:00
Matthew Meszaros 8ce331a7ac feat: fix the campaign test-email endpoint answering 404 for every caller by scoping its campaign lookup to the organization instead of the user, let it send from any mailbox of the organization and render a real contact through a new contact_id, attach the campaign's files, and extend the template preview with contact_id, campaign_id and account_id so it applies the signature, opt-out footer and plain-text rule the send path does 2026-09-04 02:58:36 -07:00
Matthew Meszaros f844c14804 feat: carry the mailbox display name in the emsg body blob of every send so a renamed sender reaches the worker on its next message instead of waiting for an inactive-active toggle, read it into the From header of the Gmail, Graph and SMTP clients with the name cached at load time as the fallback, and say so in the mailboxes guide 2026-09-04 02:58:36 -07:00
Matthew Meszaros eb85224cc8 Merge remote-tracking branch 'origin/main' into fix/contact-timeline-opaque-cursor 2026-09-04 02:57:59 -07:00
Matthew Meszaros 293226101a Merge pull request #313 from warmbly/fix/campaign-linked-segment-display
Show linked segments on the campaign Leads tab, explain empty lists, add export to leads and segments
2026-09-03 20:54:15 -07:00
Matthew Meszaros 850ff2b6a2 feat: lock the campaign row for update while replacing its linked segments so two concurrent replacements cannot commit the union of their sets, and return the status read under that lock from ReplaceForCampaign so the wake or restart reaction no longer depends on a post-commit link lookup that a concurrent detach could empty 2026-09-03 20:46:27 -07:00
Matthew Meszaros ab0c756aa2 feat: show a campaign's linked segments on the Leads tab as chips with live enrolled and held-out counts that filter the list, explain an empty list caused by an empty segment or hand-removed members and add them back in one click, add a scope-aware Export to campaign leads and segment members with a campaign-ready preset that carries lead status and engagement, write the links and the enrolment of PUT /campaigns/:id/segments in one transaction so a failed enrolment changes nothing instead of answering added 0, restart a completed campaign when a one-shot enrol adds leads, clear the manual-removal record when a contact is added by hand, count every link's members in one scan, audit sweep enrolments so open Leads tabs refresh, and document it in the campaigns and segments guides and the API reference 2026-09-03 20:38:27 -07:00
Matthew Meszaros 8aaf9e8c8c feat: say in the contacts API reference and the OpenAPI 400 description that a before value on the contact timeline that is not an RFC 3339 timestamp is rejected with a 400 rather than accepted 2026-09-03 20:28:51 -07:00
Matthew Meszaros 4bc971eecb feat: resume a campaign whose pool mixes capped and hours-closed mailboxes at the earlier of tomorrow and the closed mailbox's reopening, log the daily cap only when every usable mailbox is capped, and add the mixed-pool live regression 2026-09-03 20:26:21 -07:00
Matthew Meszaros 2f32b1b8c3 feat: bring the ContactTimelineEvent OpenAPI schema up to the real event shape with the lifecycle, form_submitted and page_hit types and the machine, link, origin, category, form and page_hit fields backed by new ContactLinkClick, EngagementOrigin and WebsitePageHit schemas, and list form_submitted and page_hit with their payloads in the contacts API reference 2026-09-03 20:21:42 -07:00
Matthew Meszaros 0981635c3a feat: reject a contact timeline cursor whose source rank names no merged table with a 400 in the handler through ContactTimelineSource.Valid, since rank zero is reserved for the legacy before bound and a rank past the last source would re-admit the events at the cursor's instant 2026-09-03 20:21:42 -07:00
Matthew Meszaros 83b0a5ede3 feat: add the cursor query parameter to the contact timeline operation in the OpenAPI spec, mark before as deprecated, describe the invalid cursor and limit 400s, and reference the shared Pagination schema from ContactTimelineResult 2026-09-03 20:19:16 -07:00
Matthew Meszaros eeed44f2ed feat: document the contact timeline's cursor parameter and pagination envelope in the contacts API reference, mark before as deprecated with why a bare timestamp can skip events at a page boundary, and say that a bad cursor or limit is a 400 2026-09-03 20:19:16 -07:00
Matthew Meszaros 8503ad4f37 feat: page the dashboard contact timeline with the server's opaque cursor from pagination.next_cursor instead of deriving a bare timestamp from the last event, in the listContactTimeline client and the useContactTimeline infinite query 2026-09-03 20:19:16 -07:00
Matthew Meszaros 5dbbaad5b5 feat: add the pagination envelope with its opaque next_cursor to the dashboard's ContactTimelineResult type and note that the top-level has_more only mirrors it 2026-09-03 20:19:16 -07:00
Matthew Meszaros 630dd4cab1 feat: add a live repository test that walks ten timeline events, eight of them at one instant across progress stamps, notes and activities, in pages of three and proves nothing is skipped or repeated, each cursor is the last event's key, the final page carries no cursor, and before still means strictly older 2026-09-03 20:19:16 -07:00
Matthew Meszaros a4665579de feat: accept an opaque cursor on GET /contacts/:id/timeline, keep before as a deprecated alias that maps onto the keyset at rank zero, and answer an invalid cursor, before or limit with a 400 instead of silently ignoring it 2026-09-03 20:19:16 -07:00
Matthew Meszaros 88d8a2ba8a feat: page every contact timeline source on a row comparison against the (at, source, id) cursor, unnest campaign progress stamps to one row per event so the limit and the logged-open and logged-click suppression apply per event, fetch one row past the page from every source, return all matching suppression entries, and encode the next cursor from the last event's key 2026-09-03 20:19:16 -07:00
Matthew Meszaros 18a1862d82 feat: rank the contact timeline's sources in models.ContactTimelineSource, give every event a ContactTimelineKey (at, source, id) with the newest-first comparison the merged sort uses, and add the standard pagination envelope to ContactTimelineResult while keeping has_more as a mirror 2026-09-03 20:19:16 -07:00
Matthew Meszaros 407a85ecf9 feat: cover the merged-feed cursor codec with a round-trip test that keeps sub-second precision and rejects a bare timestamp, a wrong-version token, bad base64 and trailing garbage 2026-09-03 20:19:16 -07:00
Matthew Meszaros 0cb4f312a0 feat: add a versioned opaque cursor codec for merged feeds to internal/utils/paging that carries an (at, source rank, id) keyset position and decodes a malformed or wrong-version token to a 400 2026-09-03 20:19:16 -07:00
Matthew Meszaros c298d898a7 feat: document in the campaigns guide what counts against a mailbox's daily budget and that a capped campaign waits for the next day rather than pausing 2026-09-03 20:18:43 -07:00
Matthew Meszaros 1c88c2196b feat: add live tests proving wake-ups leave the daily budget and min-gap untouched, real sends still count, and a capped campaign parks a wake-up with a single daily log line instead of pausing 2026-09-03 20:18:43 -07:00
Matthew Meszaros 6bc6bf4345 feat: reword the no-eligible-mailbox pause on the start endpoint, the task auto-pause, and its test to name the only cause left (a sending behaviour profile with no working days) now that budget and window gates defer 2026-09-03 20:18:43 -07:00
Matthew Meszaros 711be1f8c2 feat: defer a campaign whose mailboxes are all at their daily cap, outside their own hours, resting, or held by warmup health instead of auto-pausing it, replace the unreachable push-to-tomorrow recompute, and log the daily-cap and unavailable-pool decisions once per UTC day 2026-09-03 20:18:43 -07:00
Matthew Meszaros 620e89d528 feat: exclude action and wait steps from the workspace daily email limit by joining the step kind, since they stamp sent_at for routing without sending mail 2026-09-03 20:18:43 -07:00
Matthew Meszaros 3204cf41bd feat: apply the reserved-or-confirmed send filter to the workspace sent-today count, the advisor mailbox volume windows, and the deliverability dashboard denominator so campaign wake-ups stop inflating sent totals 2026-09-03 20:18:43 -07:00
Matthew Meszaros ec9a3147b6 feat: count a completed campaign task as a mailbox send only when it holds a step reservation or a worker-confirmed Message-ID, so the chain's deferral and pause wake-ups no longer spend the daily budget or reset the min-gap and rotation clocks (issue #306) 2026-09-03 20:18:43 -07:00
Matthew Meszaros 37d328c86a feat: add a partial index on campaign_contact_progress.dispatch_task_id so the daily send counters can look a task's reservation up without scanning progress 2026-09-03 20:18:43 -07:00
Matthew Meszaros febe324243 Merge pull request #301 from warmbly/fix/click-without-open-tracking
Count a click as an open, label scanner clicks, and record where every open and click came from
v0.3.1
2026-09-03 07:19:21 -07:00
Matthew Meszaros b9e81b6947 feat: fetch one row past the page from the per-event open and click sources of the contact timeline so a page filled by either source alone still reports that more events follow 2026-09-03 06:14:14 -07:00
Matthew Meszaros dc04ea1b52 feat: match a logged open to the step's first open inside a one-minute window on both sides so an unrelated older row cannot hide the summary event, and say in the contacts API reference that only a person's click counts as an open in the contact engagement totals 2026-09-03 05:57:31 -07:00
Matthew Meszaros 611704b365 feat: count only a person's opens in the contact engagement totals and last-opened time as analytics already does, keep a step's legacy first open on the timeline when only later opens were logged per event, and say so in the contacts API reference 2026-09-03 05:43:31 -07:00
Matthew Meszaros 701dc404a2 Merge remote-tracking branch 'origin/main' into fix/click-without-open-tracking 2026-09-03 05:22:05 -07:00
Matthew Meszaros e97477a718 feat: renumber the engagement origin and pending index migrations to 000125 and 000126 because main released 000124 for unsubscribe opt-out while this branch was open 2026-09-03 05:22:05 -07:00
Matthew Meszaros 0354e51a7a Merge pull request #303 from warmbly/feature/self-hosted-update-banner
Self-hosted update indicator and one-click updates
2026-09-03 05:13:26 -07:00
Matthew Meszaros d9527ad98a feat: reject an overflowing commit distance in the update version parser instead of clamping it, with a regression case, so a malformed build string can never suppress an available update 2026-09-03 05:04:30 -07:00