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 |
|