Matthew Meszaros
|
4fc6d1db77
|
feat: put every docker mount on one line in the generated unit, because command substitution strips the trailing newline and my previous multi-line blob mount collapsed the continuation so the node restart-looped on any filesystem-blob install, and mount BLOB_FS_ROOT only when it already exists rather than creating and recursively chowning a directory a co-located bare-metal backend owns
|
2026-09-09 05:59:44 -07:00 |
|
Matthew Meszaros
|
0a08983158
|
feat: keep the node's write path reachable and the image reference root-owned, by granting traverse on the state directory without making it writable, re-owning image-ref on every join so a machine joined by the earlier chown-everything script converges, mounting BLOB_FS_ROOT separately when blobs live under the state directory the mount no longer covers, and renaming the reserve dialog's remaining convert-to-dedicated button
|
2026-09-09 05:53:28 -07:00 |
|
Matthew Meszaros
|
92470e1528
|
feat: drop the racy liveness re-read from the placement live test, which re-queried whichever worker placement chose and so raced with the email package's live tests creating and deleting their own worker in a parallel package; that placement only ever picks a live worker is already covered by the unit tests, which need no database
|
2026-09-09 05:44:25 -07:00 |
|
Matthew Meszaros
|
65fe82c472
|
feat: stop the node container being able to choose the image root runs, by giving it a dedicated uid-1000 subdirectory to write its update target into instead of making the whole state directory writable, validating the tag it asks for before the updater acts on it, and no longer recursively chowning a directory a bare-metal install also keeps its blobs in
|
2026-09-09 05:41:16 -07:00 |
|
Matthew Meszaros
|
bfdbd77b04
|
feat: fix the eight defects the second review pass found, including three where the previous fix did not land: the bind-mounted state dir was root-owned so the node running as uid 1000 still could not write its update target, elevating the reserved-worker eviction did nothing because the rotation loop bailed on target-equals-current before the urgency was consulted, and the warmup-pool assertion was vacuous which hid that warmupPoolFor checked the subscription repo before the billing provider and answered free on a self-host install
|
2026-09-09 05:32:30 -07:00 |
|
Matthew Meszaros
|
8b83062eff
|
feat: fix ten defects the review found, the worst being that every seeder still wrote the worker columns migration 000141 moved to fleet_nodes so make dev could not seed, that the generated systemd unit used a command substitution systemd never expands so a joined machine restart-looped while the script reported success, that the state directory was not bind-mounted so the node wrote its update target inside the container where the host timer never saw it, and that placement stopped assigning warmup pool membership so paid mailboxes silently warmed in the free pool
|
2026-09-09 05:21:17 -07:00 |
|
Matthew Meszaros
|
704fecdcc0
|
feat: settle on WARMBLY_NODE_REGION as the one region variable for every role, because the join script wrote it while the worker still read WORKER_REGION, so a joined worker reported no region and the placement geography term stayed dead for exactly the machines that had been given one
|
2026-09-09 05:04:55 -07:00 |
|
Matthew Meszaros
|
de60bc3784
|
feat: let an isolated-egress reservation move a mailbox without clearing the score gain, because the reserved worker scores lower than the incumbent by construction (the incumbent holds the stickiness bonus) so the rotation loop refused the move on every tick and the organization never converged onto the worker it pays for
|
2026-09-09 05:02:53 -07:00 |
|
Matthew Meszaros
|
88992c396c
|
feat: hand the node its env base64 encoded at join instead of as a raw JSON string, because the POSIX-sh join script has no JSON parser and its sed extraction appended a stray ","heartbeat_seconds line to node.env, and refuse a heartbeat that re-registers an existing node id under the other role, which would have left a worker's mailboxes assigned to a machine that had stopped doing worker work
|
2026-09-09 05:01:13 -07:00 |
|
Matthew Meszaros
|
1a7fd85f4b
|
feat: drop the throwaway migration harness that a blanket git add swept into the placement commit, since it only existed to apply migrations against a scratch database while verifying the branch
|
2026-09-09 04:56:07 -07:00 |
|
Matthew Meszaros
|
58b7d81d33
|
Merge remote-tracking branch 'origin/main' into refactor/automatic-worker-mailbox-placement
|
2026-09-09 04:54:04 -07:00 |
|
Matthew Meszaros
|
435dbb522f
|
feat: replace the worker tier/type/risk-pool/egress categories with a scored placement model and make the fleet pull-based, so a machine joins with one command, workers and consumers share one node registry with usage and liveness, nodes self-update to the version the control plane resolves, and the Hetzner provisioning, worker profiles and SSH orchestrator are removed
|
2026-09-09 04:54:01 -07:00 |
|
Matthew Meszaros
|
51fe8f051a
|
Merge pull request #394 from warmbly/fix/round-robin-sender-attribution
feat: attribute a rotating campaign send to the mailbox that actually sent it
|
2026-09-09 04:17:42 -07:00 |
|
Matthew Meszaros
|
db2a9ba439
|
feat: stamp the rotating campaign send's real sending mailbox onto tasks.email_account_id before dispatch so budgets, round-robin position, deliverability rates and the contact feed name the mailbox that actually sent, and stop the contact email list and campaign-state steps reporting automated prefetches as recipient opens (issue #392)
|
2026-09-09 03:55:28 -07:00 |
|
Matthew Meszaros
|
3de92ca2dd
|
Merge pull request #391 from warmbly/fix/issue-380
fix: images, paste spacing, undo and HTML source in the campaign body editor
|
2026-09-08 21:32:13 -07:00 |
|
Matthew Meszaros
|
e783c3e3a0
|
feat: merge main into the email editor branch
|
2026-09-08 21:27:28 -07:00 |
|
Matthew Meszaros
|
23caf73b14
|
Merge pull request #384 from triadgit/fix/thread-on-in-reply-to
fix: thread an IMAP message on In-Reply-To only, never on the sender's Reply-To
|
2026-09-08 21:27:17 -07:00 |
|
Matthew Meszaros
|
7ad49c649a
|
Merge branch 'main' into fix/thread-on-in-reply-to
|
2026-09-08 21:22:31 -07:00 |
|
Matthew Meszaros
|
0662cac152
|
Merge pull request #390 from joaoppa/fix/reopen-verification-on-connect
Re-check what the built-in probe left unknown when a verifier is connected
|
2026-09-08 21:19:08 -07:00 |
|
Matthew Meszaros
|
1e1bb3622e
|
feat: address the CodeRabbit review on the email editor PR by generating email image object keys from a uuid and the sniffed extension instead of the uploader's filename so a name carrying '..' cannot produce a key the public route refuses to serve and a private filename never travels inside a recipient's URL, deleting the stored object before its row and refusing the request when storage will not take it so a failed delete leaves nothing loadable that the quota has stopped counting, accepting only https image addresses, resynchronising the HTML source view when a template replaces the body underneath it, and rendering the campaign preview through the inbox's sandboxed frame so markup written in the source view can neither run nor restyle the dashboard for a teammate
|
2026-09-08 21:17:36 -07:00 |
|
Matthew Meszaros
|
4f40ab7ec8
|
feat: merge main into the in-reply-to threading branch
|
2026-09-08 21:06:29 -07:00 |
|
Matthew Meszaros
|
95a4986d9e
|
feat: merge main into the verification reopen branch
|
2026-09-08 21:05:37 -07:00 |
|
Matthew Meszaros
|
e47937432f
|
feat: address the CodeRabbit review on the verification reopen PR by restricting the connection-triggered recheck to built-in verdicts so a verdict a paid verifier already produced is not re-spent when another connection is made, refusing to restore a connection the member disconnected while the credit round trip was in flight, and registering each live fixture's cleanup as it is written so a failed assertion cannot strand an integration row that then breaks the shared org fixture's teardown
|
2026-09-08 21:05:37 -07:00 |
|
Matthew Meszaros
|
9ce1e060dd
|
feat: fix the campaign step body editor for issue #380 by registering TipTap's UndoRedo and HardBreak so Ctrl+Z and Shift+Enter work at all, normalising pasted HTML from Gmail/Outlook/Word so their blank-line scaffolding stops rendering a second gap on top of our paragraph margins, adding a workspace email image library (migration 000139, public email-images/ objects sharing the attachment storage quota, keyset-paginated /email-images endpoints) with toolbar upload, URL and library insertion, drag-drop, clipboard paste and a size/alignment/alt bubble, and adding an HTML source view that names the tags the schema would drop before switching back
|
2026-09-08 21:00:09 -07:00 |
|
Matthew Meszaros
|
1661899880
|
Merge pull request #389 from warmbly/feature/bulk-select-all
feat: select all matching for bulk actions in contacts, campaign leads and segments
|
2026-09-08 20:31:06 -07:00 |
|
Matthew Meszaros
|
0e7525866a
|
feat: merge main into the select-all branch and rebuild the contact selection on top of its new keyset pagination, dropping this branch's own cursor repair now that Search carries the sort key and boundary value in a SortCursor, re-extracting the shared WHERE builder from that version, and pointing SearchIDs at the contactSorts spec and the conditional campaign-count lateral so a resolved selection orders and joins exactly the way the list does
|
2026-09-08 20:26:34 -07:00 |
|
Matthew Meszaros
|
5a1a726fdc
|
Merge branch 'main' into fix/reopen-verification-on-connect
|
2026-09-08 20:10:31 -07:00 |
|
Matthew Meszaros
|
c7a44ad909
|
feat: complete the CRM push 400 description in the OpenAPI spec with the shared resolver's own failures, a select-all carrying no filters and a filter matching more than fifty thousand contacts, so the published contract matches every way the endpoint can refuse a selection
|
2026-09-08 20:02:26 -07:00 |
|
Matthew Meszaros
|
1432e7c459
|
feat: publish the exclusion-list bound that ResolveSelection enforces by giving every OpenAPI exclude field maxItems 50000 and naming too_many_contacts in the bulk update, bulk delete and CRM push 400 descriptions, and note the cap on the selection table in the contacts API reference
|
2026-09-08 19:50:46 -07:00 |
|
Matthew Meszaros
|
5fed6bee59
|
Merge pull request #388 from warmbly/feat/campaign-entry-delay
feat: delay before the first email in a campaign
|
2026-09-08 19:39:45 -07:00 |
|
Matthew Meszaros
|
29b479321a
|
feat: merge main and renumber the campaign entry-delay migration to 000138, since main released 000137_contacts_org_created_index while this branch was waiting to merge
|
2026-09-08 09:59:43 -07:00 |
|
Matthew Meszaros
|
c8042e2942
|
Merge remote-tracking branch 'origin/main' into feat/campaign-entry-delay
|
2026-09-08 09:58:38 -07:00 |
|
Matthew Meszaros
|
5b6667e39e
|
Merge pull request #387 from warmbly/fix/issue-382-user-experience
fix: campaign Leads tab and contact export stop partway through a large list
|
2026-09-08 09:49:26 -07:00 |
|
joao-crm
|
83ba4b38b4
|
fix: re-check the addresses the built-in probe left unknown as soon as a workspace connects a paid verifier, and withdraw the connection's degraded state once that verifier answers again, since a verdict reached before the connection existed otherwise waited out the 30-day shelf life while the credits sat unused and the card kept reporting whatever the provider said on the very first pass
|
2026-09-08 15:48:12 +00:00 |
|
Matthew Meszaros
|
a62652de3a
|
feat: merge main and renumber the contacts index migration to 000137, since main released its own 000136 for lead sync segments while this branch was in review and two branches that each take the next number are green alone but leave golang-migrate refusing to build its source driver once both land
|
2026-09-08 08:34:52 -07:00 |
|
Matthew Meszaros
|
c35495ae5c
|
feat: await the trigger card's recovery refetch inside the same promise chain as its saves, so a refused entry-delay commit's invalidation cannot resolve after the next commit's optimistic write and put the stale delay back on the card
|
2026-09-08 08:34:20 -07:00 |
|
Matthew Meszaros
|
37ed967919
|
Merge remote-tracking branch 'origin/main' into fix/issue-382-user-experience
|
2026-09-08 08:32:31 -07:00 |
|
Matthew Meszaros
|
073bb3c6bc
|
feat: merge main and renumber the campaign entry-delay migration to 000137, since main released 000136_lead_sync_segments while this branch was open and a duplicate version makes golang-migrate refuse to build its source driver, which is what failed the Migrations job and the check-migrations prerequisite inside make lint on Go CI
|
2026-09-08 07:33:21 -07:00 |
|
Matthew Meszaros
|
4e52e9476b
|
Merge remote-tracking branch 'origin/main' into feat/campaign-entry-delay
|
2026-09-08 07:31:31 -07:00 |
|
Matthew Meszaros
|
f8c0899be4
|
feat: address the CodeRabbit review by promoting a rounded wait into the next unit so a contact drawer never reads 60 minutes or 24 hours, describing the entry delay's anchor as the lead's own enrolment moment with the campaign's creation date only as the legacy fallback in both the campaigns and segments guides, replacing the launch dialog's 'sending begins immediately' line when a delay is set, following an outside value change with the picker's preset or custom mode, and chaining the trigger card's saves so two commits inside one round trip cannot land out of order
|
2026-09-08 07:30:24 -07:00 |
|
Matthew Meszaros
|
f6604c08d2
|
Merge pull request #386 from warmbly/fix/issue-381-user-experience
feat: pin imported contacts into the segment they were imported from (#381)
|
2026-09-08 06:59:23 -07:00 |
|
Matthew Meszaros
|
0f83329936
|
feat: answer the CodeRabbit re-review by guaranteeing the promise the failed-pin warning makes: an import-level note now goes to the front of the error list and is never dropped by the per-row cap, so a file full of bad addresses cannot bury or evict the one note explaining why the rows that did import are not in the segment they were imported into, with a single note however many segments were targeted and a live test that forces the write to fail behind a capped error list
|
2026-09-08 06:54:36 -07:00 |
|
Matthew Meszaros
|
78dd032a06
|
feat: address the CodeRabbit review on the contacts pagination PR by refusing a cursor boundary the SQL cast would choke on so a hand-made token is a 400 instead of a database error surfacing as a 500, writing timestamp boundaries with an explicit to_char pattern the Go validator mirrors so a token never depends on the server DateStyle, basing the full-screen error guard on rows loaded rather than rows left after the client-side subscription filter, keeping the failure visible when that filter hides every loaded row, and pointing Try again at the request that actually failed by reading isFetchNextPageError instead of guessing from hasNextPage
|
2026-09-08 06:53:57 -07:00 |
|
Matthew Meszaros
|
177e36265d
|
Merge remote-tracking branch 'origin/main' into fix/issue-381-user-experience
|
2026-09-08 06:47:37 -07:00 |
|
Matthew Meszaros
|
03b6188364
|
feat: tighten the import result's failed-pin warning so it contrasts the two outcomes instead of joining them with an and that reads as if both succeeded
|
2026-09-08 06:36:34 -07:00 |
|
Matthew Meszaros
|
a453573c7b
|
feat: finish the OpenAPI half of the contact selection change by rewriting the bulk delete, bulk update and CRM push operation and 400 descriptions for the filter form and its selection_too_large and empty-match failures, and encoding the two valid bodies as a oneOf so a generated client cannot send an empty object, a select-all with no filters, or an id list and a filter at once
|
2026-09-08 06:17:36 -07:00 |
|
Matthew Meszaros
|
687aa6ec5e
|
feat: page through the contact list under a text query, the min and max campaign filters and a segment scope in the pagination regression test, since each appends bound parameters either side of the cursor's or compiles a SQL fragment of its own and a shifted placeholder would otherwise only show up in production
|
2026-09-08 06:00:16 -07:00 |
|
Matthew Meszaros
|
f46728733e
|
feat: address the CodeRabbit review on the select-all PR by tying the header checkbox to the rows on screen and making it put unticked rows back before it clears a select-all, bounding the exclusion list before it sizes a map, skipping half-filled custom-field pills when a filter is saved as a segment so the segment matches the search, and naming the fifty-thousand selection cap on the campaigns and segments guides
|
2026-09-08 05:39:16 -07:00 |
|
Matthew Meszaros
|
c59446271b
|
Merge remote-tracking branch 'origin/main' into feature/bulk-select-all
|
2026-09-08 05:20:02 -07:00 |
|
Matthew Meszaros
|
a6862477b3
|
feat: add a 'select all matching' selection to every contact list so a bulk action can cover the whole filtered set instead of only the loaded pages: a ContactSelection body (all + filters + exclude) on bulk update, bulk delete, verification, batch research, segment members and CRM push resolved server-side through a new SearchIDs that shares the search's WHERE builder, a Gmail-style banner and shared selection module in the contacts table, campaign Leads and segment members views, an uncapped select-all in the From contacts picker, and fixes for the keyset cursor comparing a row against itself (contact pagination repeated and skipped rows past page one) and the dashboard sending custom-field filters under the wrong wire name
|
2026-09-08 05:19:59 -07:00 |
|