Matthew Meszaros
ade86d1b86
feat: scope the tag, category and folder registries plus unibox conversation labels to the organization instead of the creating user (migration 000147), so a teammate sees and can edit the labels the owner made ( #436 ), and guard every label write in email/campaign/contact/form/unibox repositories against ids belonging to another workspace
2026-09-12 03:30:36 -07:00
Matthew Meszaros
5869a2148f
feat: stop a campaign sending outside its sending window, and past its end date, when a follow-up is overdue: the placer's schedule gates are asked about the moment a step became due, and nextScheduleSlot deliberately returns an instant that was already inside a window unchanged, so a step that came due at 2pm still read as due at 11pm and the task sent it there, while the end-date comparison found a candidate predating the end date; floor an overdue candidate at now so the window, the weekday mask and the end date are all asked about the send that is actually about to happen, with live tests for a closed window, a passed end date and the overdue step that must still go out promptly while the window is open
2026-09-12 03:29:33 -07:00
Matthew Meszaros
7754dac629
Merge pull request #458 from warmbly/feature/contact-page-real-data
...
feat: rebuild the marketing contact page with real channels only
2026-09-12 03:15:04 -07:00
Matthew Meszaros
5857a6ddd8
Merge remote-tracking branch 'origin/main' into feature/contact-page-real-data
2026-09-12 03:13:42 -07:00
Matthew Meszaros
47defafa09
feat: fix the six self-host defects reported in issue #439 ( #456 )
...
* feat: fix the six defects reported in issue #439 by mapping the IMAP UNAVAILABLE, INUSE and NONEXISTENT response codes to retry-level errors instead of a critical reconnect prompt, synthesising a stable no-msgid key so one message with no Message-ID header can no longer 400 the internal map endpoint and wedge every later sync pass with its cursors held, adding mailhtml.FromText and HasContent so an API or agent-created step with a plain body stops shipping the composer's empty div placeholder as its text/html part (derived on create and plain-only update, exposed as body_html on update_campaign_step, dropped at send and preview time, and refused at campaign start with empty_step_body), honouring sender_strategy='explicit' in ResolveCampaignSenderPool and ValidateCampaignReady so an emptied explicit pool parks the campaign instead of widening it to every mailbox in the workspace, making the paused_no_accounts auto-pause loud with an error log line, an error-level activity-feed entry and an org-scoped CAMPAIGN_PAUSED realtime pulse, gating the admin sign-in's Turnstile widget on GET /v1/auth/config so a self-host with CAPTCHA_PROVIDER=none is not locked out, and parsing NATS_URL down to its host:port so a credentialed bus URL no longer reports NATS down
* feat: act on the self-review of the issue #439 fixes by dropping the campaign wizard's own escapeHtml body_html builder, which entity-escaped the quotes in a conditional and made the template fail to parse at send time, and letting the backend's FromText render that part instead so wizard-written steps also get their bare URLs linked for click tracking, correcting the docs and openapi description that claimed an explicit sender pool never falls back when it still unions its tags as migration 000013 designed, extracting the duplicated blank-HTML-part guard into dropBlankHTMLPart shared by the send path and the preview, and recording why the no-msgid key keeps the folder name despite a RENAME changing it
* feat: address the CodeRabbit review on the issue #439 fixes by holding the admin sign-in's Turnstile widget unmounted until /v1/auth/config resolves so an instance with no route to Cloudflare cannot raise a widget error on a screen nobody submitted, failing StartCampaign closed when the sequence read errors rather than skipping both the malformed-template and empty-body refusals, giving TCPCheck the default port its protocol assumes so a portless NATS_URL is no longer reported down, leaving a URL that carries a merge field unanchored because the send path renders bodies with text/template and a quoted contact value would break out of the href, and correcting the sequences guide and the Campaign and CampaignUpdate openapi descriptions that named the wrong tag field
2026-09-12 03:13:38 -07:00
Matthew Meszaros
9ce576bb3f
feat: rebuild the marketing contact page as a minimal list of real channels (team@warmbly.com, the Discord, GitHub issues and discussions, docs, status) and the Mindroot Ltd registration, dropping the invented Delaware entity, postal address, team locations, business hours and SLA
2026-09-12 03:02:57 -07:00
Matthew Meszaros
dc9ce403de
feat: stop one un-sendable lead parking a whole campaign and stop the contact drawer's next-action time walking forward on every refresh (issue #437 ): route up to config.CampaignPlacementCandidates due leads per pass instead of one, classify a placement refusal that belongs to a single lead (ESP-strict finding no mailbox for that recipient's provider, a bound lead inside its own mailbox's minimum gap or waiting for it to reopen, a recipient's send-time-optimized hours) as the new ErrLeadDeferred so the pass moves to the lead behind them and only defers the campaign when every candidate is refused, log the ESP-strict deferral once a day rather than once per refused lead per tick, and make PreviewContactSend a pure read that answers unchanged state identically on every call by running placement with the even-distribution, jitter, conflict-resolution, distribution-curve and sub-minute layers off, taking a behaviour profile's gap at its floor instead of drawing it, picking the mailbox deterministically instead of re-rolling rotation, reporting the next sending day's first open minute instead of a jittered twenty-four-hours-from-now, and reporting a due step's time as the campaign chain's own stored wakeup
2026-09-12 02:58:48 -07:00
Matthew Meszaros
b0050507e4
fix: tell a joining node a tag that exists, falling back to the published prod tag instead of a latest this project has never published, and drop the WORKER_IMAGE docs left behind by the removed push-based provisioning ( #455 )
v0.4.5
2026-09-12 01:00:11 -07:00
Matthew Meszaros
26f4549f63
fix: redact the credential from the Redis URL the realtime service logs on every boot, keeping the address that makes the line useful ( #454 )
2026-09-12 00:52:18 -07:00
Matthew Meszaros
ca1c10ec72
fix: give the realtime Redis event bridge the same wildcard TLS options as the command pool, because it is a second connection that a managed Redis rejects and it is the one that delivers every realtime event ( #453 )
2026-09-12 00:41:30 -07:00
Matthew Meszaros
a4f503f620
fix: realtime cannot complete either of its TLS connections ( #452 )
...
* fix: let the realtime service actually establish its TLS connections, adding the wildcard hostname match fun Redix needs for every managed Redis and a CA bundle option for an RDS Postgres that the system trust store cannot verify
* feat: drop the redundant wget package from the realtime image since busybox already provides it, and fix a comment that read as literal interpolation
* fix: treat a blank DATABASE_SSL_CA_FILE as unset in the realtime config, because compose passes every optional variable through as empty and cacertfile with an empty path is no CA source at all rather than a fallback to the system store
2026-09-12 00:29:34 -07:00
Matthew Meszaros
ee9e8706fb
feat: build librdkafka with OpenSSL in the tracking service so security.protocol=SASL_SSL works, which every managed broker requires and which the cmake-build-only feature set silently disabled ( #451 )
2026-09-11 23:37:20 -07:00
Matthew Meszaros
fa252aebe8
Merge pull request #443 from warmbly/feature/editor-image-links-and-buttons
...
feat: clickable images and a call-to-action button in the campaign body editor
2026-09-11 23:06:29 -07:00
Matthew Meszaros
8a2856fce0
Merge branch 'main' into feature/editor-image-links-and-buttons
2026-09-11 23:02:15 -07:00
Matthew Meszaros
e8186e5f52
fix: the tracking service must not write Avro to a JSON consumer ( #450 )
...
* feat: make the Rust tracking publisher honour CODEC_PROVIDER on Kafka instead of always writing Avro, so a json consumer stops silently dropping every open and click, and refuse avro at boot when no Schema Registry is configured
* feat: build the tracking service's kafka feature in CI, because clippy on the default build never opens kafka.rs and that file now ships as the published tracking -kafka image
* feat: install libcurl and the rest of the librdkafka build dependencies for the tracking kafka clippy step, which fails at the first object without curl headers even with WITH_CURL=0
2026-09-11 23:00:15 -07:00
Matthew Meszaros
ea8d15374e
Merge branch 'main' into feature/editor-image-links-and-buttons
2026-09-11 22:56:16 -07:00
Matthew Meszaros
184cc951f0
Merge pull request #444 from warmbly/fix/issue-432
...
fix: make Edit with AI apply the instruction instead of rewriting the body into a fresh cold email
2026-09-11 22:52:54 -07:00
Matthew Meszaros
88b483d227
Merge branch 'main' into fix/issue-432
2026-09-11 22:47:54 -07:00
Matthew Meszaros
7d79dcc282
feat: append a -kafka image variant to every version the control plane hands a fleet node when the instance runs Kafka, so a joining worker pulls a build that can actually reach the bus instead of failing at boot ( #449 )
2026-09-11 22:46:02 -07:00
Matthew Meszaros
1f1112fe93
feat: drop the AI edit's parked review selection when the popover closes, because a result that changes nothing commits no new value and so leaves the layout effect nothing to run on, and the range would then be worn by whatever unrelated edit commits next and yank the caret back into a rewrite the user had already dismissed
2026-09-11 22:42:50 -07:00
Matthew Meszaros
f41eac289d
Merge branch 'main' into fix/issue-432
2026-09-11 22:35:43 -07:00
Matthew Meszaros
20b13dcea7
Merge branch 'main' into feature/editor-image-links-and-buttons
2026-09-11 22:34:38 -07:00
Matthew Meszaros
280a3e64ac
feat: publish Kafka-linked image variants ( #448 )
...
* feat: publish Kafka-linked images for backend, consumer, worker and tracking as -kafka tag variants built per-arch on native runners, because the cgo librdkafka link cannot cross-compile on the build-go path
* feat: carry BUILT_AT inside the Go Kafka targets' build-args instead of the shared build-native block, so tracking and realtime stop warning about an unconsumed arg, and emit the matrix with printf because echo expands the separating backslash-n in some shells
2026-09-11 22:31:41 -07:00
Matthew Meszaros
4bf412c226
feat: decide the AI edit's No change signal from the value the composer will actually hold rather than from the model's answer, since a rewrite whose every added character falls past the body-length cap leaves the textarea exactly as it was and reporting Rewritten over an unchanged body is the one thing that signal exists to prevent
2026-09-11 22:30:32 -07:00
Matthew Meszaros
a3aec0a9cb
Merge branch 'main' into feature/editor-image-links-and-buttons
2026-09-11 22:27:11 -07:00
Matthew Meszaros
ea321ebefb
feat: put the AI edit's review selection back after React commits the value rather than before it, since React writes the textarea's value during commit and that write moves the cursor to the end, leaving the rewrite unselected and Undo's restored range never reaching the DOM, and strengthen the Undo test to assert the range a follow-up run targets instead of the restored value, which Undo sets regardless and which therefore pinned none of the behaviour the commit before it fixed
2026-09-11 22:22:55 -07:00
Matthew Meszaros
d42aeea1aa
Merge pull request #447 from warmbly/feat/kafka-topic-creation
...
feat: the Kafka bus creates the topics it uses
2026-09-11 21:59:41 -07:00
Matthew Meszaros
e3092f2322
feat: address the review by releasing the topic lock before the broker call so one slow creation cannot stall every publish, subscribe and close for the full admin timeout, refusing to open an admin connection once the bus is closed so a publish racing past the closed check cannot resurrect a client nothing will shut, building rather than vetting the tagged Kafka backend in CI because vet does not link and a CGO backend fails at link time, and correcting the Confluent tier wording to say auto topic creation is configurable only on Dedicated
2026-09-12 06:54:34 +02:00
Matthew Meszaros
af7256ac85
feat: address the third review pass on the Edit with AI fix by recording the range a rewrite replaced instead of working it back out of the lengths afterwards, because the composer's body cap can cut the tail off what was written and the old arithmetic then reconstructed a range that was never selected, so Again re-sent the wrong words and Undo restored the wrong selection, with a test mounting the real textarea host against a small cap to pin both
2026-09-11 21:33:46 -07:00
Matthew Meszaros
069ab15194
feat: address the second review pass on the Edit with AI fix by reading a markdown destination with balanced parentheses so a link the model normalises out of the angle-bracket form it was given comes back whole instead of truncated at the first paren with a stray one left in the copy, restoring only spaces and tabs around the model's answer so a selection that ran to the start of the next paragraph joins the two the way a paste would rather than gaining a blank paragraph nobody typed, and capping the completion at a flat 4096 tokens, above the 3072 it was and below the smallest completion limit in common use, because a request over a backend's own cap earns a 400 naming max_tokens that openAIProvider.adaptParams latches for the life of the process and degrades every later call
2026-09-11 21:21:40 -07:00
Matthew Meszaros
6504d9958a
feat: create Kafka topics from the bus that uses them, because a worker's command topic is named after the node id issued at join time so the set is not knowable in advance, and the broker's auto-creation is off by default on Confluent Cloud and not configurable below Standard, which left a worker subscribed to a topic that did not exist receiving nothing and reporting no error, and compile the tagged Kafka build in CI so a backend nothing else builds cannot rot unnoticed
2026-09-12 06:02:49 +02:00
Matthew Meszaros
704e793c8a
Merge branch 'main' into feature/editor-image-links-and-buttons
2026-09-11 20:57:20 -07:00
Matthew Meszaros
74771004c4
Merge pull request #446 from warmbly/feat/nats-max-bytes
...
feat: a stream size ceiling, and a latent bug in the retry path
2026-09-11 20:45:19 -07:00
Matthew Meszaros
131e9ff093
feat: give the JetStream stream a size ceiling from NATS_MAX_BYTES, accepting a byte count or a size like 2GiB, because a managed account can require every stream to declare one and Synadia's Max Bytes Required rejects creation without it, turning that refusal into an error naming the variable to set, and fix the retry path which passed a zero max age and would have recreated the stream with no age limit after a failure
2026-09-12 05:40:43 +02:00
Matthew Meszaros
f7e1deeae4
Merge pull request #445 from warmbly/feat/nats-credentials
...
feat: support NATS JWT credentials, so a managed bus is an option
2026-09-11 20:34:10 -07:00
Matthew Meszaros
f1f5249153
feat: authenticate to NATS with a user JWT and nkey seed so a managed bus like Synadia Cloud can replace a self-run one, taking the credential from a file path for containers and from a single-line base64 value for the fleet, because a node receives environment variables rather than files and the env file docker reads cannot express the multi-line credentials format, in both the Go event bus and the Rust tracking publisher
2026-09-12 05:29:09 +02:00
Matthew Meszaros
1bdd1da800
feat: address the CodeRabbit review on the Edit with AI fix by leaving the caret after text written at a collapsed position instead of in front of it (an insertion maps to itself unless the position associates rightwards, so continuing to type went before the insert), splitting the model's blocks one separator at a time so a blank paragraph the author used as spacing survives a rewrite instead of being swallowed by a greedy newline run, carrying a link destination that holds a paren or a space through in markdown's angle-bracket form rather than dropping the link, putting the author's boundary whitespace back on the model's trimmed answer in both hosts so a selection ending on a space does not glue the rewrite to the next word and "did anything change?" compares exactly what was written, and sizing the completion cap from the passage's own rune count so an 8000-rune CJK body is not truncated by a cap chosen for English
2026-09-11 20:23:53 -07:00
Matthew Meszaros
63440e9abb
feat: pick a button's label colour by comparing both contrast ratios instead of testing luminance against a threshold, move the sky, emerald and amber swatches to the shade where a white label clears 4.5:1, and clamp the floating node bar inside every viewport edge rather than only the right one
2026-09-11 20:16:07 -07:00
Matthew Meszaros
6501cb599c
feat: fix the "Edit with AI" rewrite in the campaign body and the unibox composer for issue #432 by running /generation/edit on a new generation.BuildEditRules system prompt through AIProvider.Complete instead of the cold-outreach writer prompt that redefined the model's role, capped it at 80 words and imposed a five-part email skeleton on every instruction, raising the completion cap so a full-body rewrite is no longer truncated at 1024 tokens and counting the request limits in runes rather than bytes, carrying merge variables, AI blocks, conditionals, form links and link destinations through the round trip in web/src/components/app/ai/richTextPassage.ts instead of deleting every atom node via doc.textBetween, replacing the passage with paste semantics so a phrase rewritten inside a sentence stops splitting its paragraph into three, saying "No change" when the model hands the passage back untouched, and clamping the floating AI card to the surface it is editing so it no longer draws outside the step drawer over the flow canvas
2026-09-11 20:04:32 -07:00
Matthew Meszaros
3ee636e2e6
Merge branch 'main' into feature/editor-image-links-and-buttons
2026-09-11 20:01:18 -07:00
Matthew Meszaros
7ff9f4d2c6
feat: let a campaign body image carry a link and add a call-to-action button to the editor toolbar, both rendered as mail-safe markup (an anchor-wrapped img, a one-cell table whose cell holds the colour and padding for Outlook's Word engine), with the plain-text half now keeping every link destination
2026-09-11 20:01:16 -07:00
Matthew Meszaros
f4581108ca
Merge pull request #441 from warmbly/fix/issue-434
...
feat: per-step open, click, reply and bounce rates in Step performance
2026-09-11 10:21:04 -07:00
Matthew Meszaros
470654f5b0
feat: say machine_clicks counts the contacts whose only clicks on a step were automated rather than counting steps, document the zero-send rule on all four step rates in the OpenAPI schema, and stop get_campaign_stats dropping the machine open and click counts from both the campaign totals and each step
2026-09-11 09:12:22 -07:00
Matthew Meszaros
35d9d3d612
feat: count automated clicks from one rolled-up bool_and(machine) per step and contact joined into both the campaign summary and the per-step stats, instead of a correlated EXISTS pair per progress row, which measured 78ms against 33ms on a 50k-lead campaign with 20k logged clicks
2026-09-11 09:01:17 -07:00
Matthew Meszaros
53d25baa29
feat: read the step performance hover as 'Opens: 1 · 20.0% of 5 sent' instead of the mis-pluralised '1 opens', by labelling each StepMetric cell with its capitalised metric name and putting the count after the colon
2026-09-11 08:57:52 -07:00
Matthew Meszaros
5087023e48
feat: give every campaign step its own open, click, reply and bounce rate in Step performance, computed against that step's own sends, with the automated share of its opens and clicks carried alongside them through GetSequenceStats, the campaign analytics API, the get_campaign_stats AI tool and the docs
2026-09-11 08:56:03 -07:00
Matthew Meszaros
a542bb2c9c
Merge pull request #427 from chrisedington/feat/cleanmylist-verification
...
Add CleanMyList email verification integration
2026-09-11 06:17:37 -07:00
Matthew Meszaros
a5b0e2c2f3
Merge branch 'main' into feat/cleanmylist-verification
2026-09-11 06:12:10 -07:00
Matthew Meszaros
87e3f7006d
Merge pull request #438 from warmbly/fix/scanner-timing-window
...
feat: operator-editable automated-engagement windows, and Barracuda in the scanner catalogue (#412 follow-up)
2026-09-11 06:09:06 -07:00
Matthew Meszaros
a8c66591ce
feat: correct the isInstant comment that claimed a backwards-skewed clock must not mark events human when the code does exactly that, describing the real behaviour instead: an event stamped before its dispatch means the clocks disagree, so the timing rule abstains and leaves the verdict to the user agent and source network
2026-09-11 06:04:38 -07:00