Build log

Everything we ship, one commit at a time — building in public.

BUILD LOGUsebrandidentity.com/logo/{domain}for every issue, includingthe network digest.
Use brandidentity.com/logo/{domain} for every issue, including the network digest.

Stop defaulting aggregate logos to ventureos.com; rewrite baked-in ventureos logos when serving archive pages and when sending so existing issues like aggregate-20260629 show newsletter.org.

8/3/2026a683bfa
BUILD LOGFix theme previews to useeach brand’s logo frombrandidentity.com.
Fix theme previews to use each brand’s logo from brandidentity.com.

Site Config and issue theme samples no longer hardcode ventureos.com; the preview passes the config domain so logos and sample art match the brand.

8/3/2026eef9ee9
BUILD LOGRefactor issue managementand scheduling: Updatetext for clarity on weeklyissues and site
Refactor issue management and scheduling: Update text for clarity on weekly issues and site configurations. Implement staggered scheduling for brands to avoid conflicts, ensuring drafts are auto-composed and requiring approval before sending. Enhance settings management with new UI elements for better user experience and add functionality for staggered schedules. Update API routes to support new scheduling logic and ensure all configurations default to draft-only mode.
8/3/2026d4c89cb
BUILD LOGPostmortem: June 29mass-send was a crash, nota bounce storm
Postmortem: June 29 mass-send was a crash, not a bounce storm

333,795 'failed' rows in newsletter_sends explained. All 9,800 'sent' rows carry SES message ids and went out in one 53-minute burst; none of the 333,795 'failed' rows have an SES id — so they never reached SES. Signature of a serverless timeout that drained ~9,800 then bulk-marked the rest failed, not undeliverable email. The 333k were never delivered and produced no bounces. The real exposure is the list: 344,789 vnoc_lead subscribers imported in one day with only 80 marked invalid (0.02%) — hygiene (lib/qualify.ts) never ran. A fixed send loop against this same list would deliver all 344k and then bounce at scale. The crash masked the risk; it didn't remove it. Gaps: newsletter_sends has no error column, the send loop isn't chunked/ resumable, and the import bypassed hygiene. Hard gates listed before any resend. Creating per-domain drafts stays safe (composes only).

7/24/2026f4dfcc7
BUILD LOGCorrect SES diagnostic;iPartner transactional nowisolated
Correct SES diagnostic; iPartner transactional now isolated

Correction: the earlier claim that no code passes a configuration set was wrong. It grepped only lib/ses.ts (the client singleton) — newsletter.org sends from lib/issues.ts, which does pass ConfigurationSetName and sets both List-Unsubscribe headers. That track was already correct. The real gap was iPartner's transactional path, now fixed: new ipartner-transactional config set (reputation metrics, suppression, TlsPolicy=REQUIRE) with an SNS event destination, and ConfigurationSetName threaded through sendEmail(). Verified by live send and by falsification — a bogus config set raises ConfigurationSetDoesNotExistException. Still unverified: whether anything subscribes to the SNS topic. The IAM user lacks SNS:ListSubscriptions, so events may still be going nowhere.

7/24/2026de25299
BUILD LOGDiagnose SES probation:clean now, historicalcause, instrumentationinert
Diagnose SES probation: clean now, historical cause, instrumentation inert

Current sending is healthy — 2,794 sent over 14 days, 0.18% bounce, 0.000% complaint — so probation is not caused by present behaviour. Suppression entries cluster in Jan 27-Mar 19 2026, pointing at a Q1 incident that was never cleared. Main finding: the newsletter-events configuration set is fully wired (reputation metrics, suppression, VDM, SNS destination on bounce/complaint/ delivery/open/click) but no code passes ConfigurationSetName — not in newsletter.org/lib/ses.ts nor ipartners-nextjs/src/lib/ses.ts. So the SNS topic receives nothing, transactional and bulk share one pooled reputation, and no reputation problem can be attributed to a sender. Includes the console checks that need a human (CLI returns only ~14 days of send stats, and the available IAM user lacks cloudwatch:GetMetricStatistics) plus the remediation order: attach config sets, then request review.

7/24/2026d51a5c3
BUILD LOGPer-lead spec: SESguardrails + signalownership
Per-lead spec: SES guardrails + signal ownership

SES account is on EnforcementStatus=PROBATION — AWS is already reviewing it, and the next step is SHUTDOWN, which would take down sign-in codes and every other sender in the estate. Added as a hard gate before any ramp; the exact bounce/complaint rates still need a console check (the available IAM user lacks cloudwatch:GetMetricStatistics). Lean on SES-native controls rather than hand-rolled ones: account suppression and VDM are already enabled; add a per-track configuration set for reputation isolation, wire bounce/complaint events to SNS, and verify one-click unsubscribe plus SPF/DKIM/DMARC for the Google/Yahoo bulk sender rules. Signal ownership: leads belongs to newsletter.org, offers to vnoc.growagent.com, partners to ipartner.com. This track reads leads only.

7/24/20269d834ae
BUILD LOGPer-lead spec: leads isthe only demand signal,offers is off-limits
Per-lead spec: leads is the only demand signal, offers is off-limits

Tighten the guardrail from 'never state offers' to 'never touch it'. The per-lead track reads and reasons about leads only.

7/24/202670481f5
BUILD LOGSpec per-lead engagementtrack
Spec per-lead engagement track

A second sending track alongside the weekly newsletter: engage each lead as it arrives, personalised from its own domain's traffic, search demand, and brand read. Measured the estate first — 1,056,226 leads across 15,397 domains, ~950 arriving daily, and in_campaign=0, so not one has ever been contacted. The existing >=1000-leads-per-domain gate covers 126 domains and is the right trigger for a weekly issue, the wrong one for responding to a signup. Spec is shaped by the constraint that 90% of the list is >2 years cold: fresh-90-days only at launch, subscribed treated as suppression rather than consent, sender split so a bulk send can never take down sign-in codes, and reply handling required before the first send.

7/24/20268416420
BUILD LOGEnhance admin issuemanagement by integratingissue statusreconciliation, updating
Enhance admin issue management by integrating issue status reconciliation, updating issue detail pages to display failed sends, and improving stats page to include failure metrics. Adjusted query logic for better data retrieval and refined UI elements for clarity.
6/29/20262c9bd1d