The feature was implemented and landed on main by earlier work (859e2cbd, ca2eff3b, ac6dc40b, a9d0c8dc, 3d4cd85f, ce8bd641, c35ca843 and the knowledge-repo template migration). This session re-verified every acceptance criterion on current origin/main (35d6f21c + eec18f6f), closed the last docs drift, and re-ran the tests. No product code changes were needed.
- AC1 metric fields + DB migration:
NewsletterIssueStats(sent, opens, clicks, uniqueClicks, openRate, clickRate) on the issue and per-slotNewsletterSlotStats(clicks, uniqueClicks, clickRate, linkedin impressions/clicks/reactions/ctr/postUrl, x impressions/clicks/reactions/ctr/postUrl) inbackend/src/db/newsletterIssues.ts; API validation inbackend/src/routes/newsletterIssues.ts. Migration: thedeliveries→sentrename script (ca2eff3b) was applied in prod (41 newsletter issues migrated) and then deleted per the no-dead-code rule (63a25a86). - AC2 CLI:
dataops newsletter metrics <issue-id> [primary|secondary]with--sent --opens --open-rate --clicks --unique-clicks --click-rate --linkedin-impressions --linkedin-clicks --linkedin-reactions --linkedin-ctr --linkedin-post --x-* --pull-mailchimp --clear;--helpdocuments it (cli/src/commands/newsletter.mjs); with no metric flags it prints the current values (read form). Testcli/test/work.test.mjs"newsletter metrics and sponsor report" passes. - AC3 UI:
frontend/src/surfaces/planning-newsletter-slots.jsrenders the Mailchimp line (sent, opens + rate, clicks + unique + CTR) and per-slot LinkedIn/X lines with post links in the issue view/dialog.frontend/test/planning-surface.test.mjs30/30 pass. - AC4 sponsor report:
backend/src/newsletter/statsReport.tsbuilds the report and prefilled email message pertemplate.newsletter.newsletter-performance; served atGET /api/newsletter-issues/:id/reportandGET /api/newsletter-issues/report?sponsor=; CLIdataops newsletter report. - AC5 task rename + migration + auto-close: open-card migration applied in prod (41
issues; script
ca2eff3b, deleted after apply63a25a86). The live knowledge-repo template now emitsblocks/sponsor-performance.yaml: collect-mailchimp-stats / collect-x-stats / collect-linkedin-stats / send-performance-to-sponsor, each collect task instructingdataops newsletter metricsas its operator action. Auto-close is wired inbackend/src/routes/newsletterIssues.ts(maybeCloseMetricsTaskrecords the stats summary as external-status proof; the retiredadd-newsletter-performanceguard was removed inc35ca843once no open cards carried it). This session updatedbackend/docs/templates/part-1-recurring-publication-templates.mdto list the live per-channel collect tasks instead of the retired single task (commit eec18f6f). Tests:backend/tests/newsletter-issues.test.tsauto-close + Mailchimp pull cases pass. - AC6 Mailchimp auto-pull (bonus): implemented after the triage note —
backend/src/newsletter/mailchimpMetrics.ts,POST /api/newsletter-issues/:id/mailchimp-metrics, CLI--pull-mailchimp [--campaign] [--primary-url/--secondary-url] [--dry-run](ce8bd641, 2026-10-30); issues resolve by number (299/#299). Backend tests cover dry-run and live pull + auto-close; no separate follow-up intake needed. - AC7 interim data: preserved in place ("leave them"). Orin's metrics comment on task
#299 is untouched by any code path; issue 299's metrics were recorded in prod via
dataops newsletter metrics 299in earlier sessions (referenced by triage). The LinkedIn metrics for issue 299 remain pending the external LinkedIn export (carried from triage; the X/LinkedIn fields and CLI flags exist for when it arrives). - AC8 tests/CI/deploy: local on origin/main — backend newsletter-issues 20/20,
CLI 460/461, frontend planning-surface 30/30. The remaining local failures are
Windows-checkout environment artifacts, all outside this intake: 8 backend
(CRLF byte-parsing of
infra/template.full.yamlunder core.autocrlf=true, missing../aws-infrasibling, missing../dataops-knowledgesibling in this worktree,/bin/shmemory-oracle spawn), 1 CLI (invoice-pdf chmod 0o666≠0o600 Windows semantics), 2 frontend (CRLF vs\nregex in architecture/coverage contract — same class ae09f50b fixed). CI on Linux is green: "Deploy DataOps V1 Lambda" run 41443058308 success @ 5e6720d7 — that workflow runs frontend coverage, backend tests, MCP tests, and the transactions gate before deploying, so the full suite passed in CI; Validate CLI success @ b50cd86d; no backend changes after 5e6720d7, so prod runs the newsletter backend. Prodhttps://ops.dtcdev.click/api/health→{"status":"ok"}.
BLOCKER (closed 2026-11-05, see addendum): AC8's interactive live proof — an
authenticated dataops newsletter metrics 299 read against prod plus a
logged-in UI screenshot/HTML check — needed an operator/Orin-scoped credential.
The 2026-11-05 session performed the authenticated prod reads (read-only GETs)
and closed this blocker; the earlier session's desktop simply had no CLI access.
All acceptance criteria re-verified on current origin/main; no product code
changes needed. Targeted tests re-run in this worktree: backend
tests/newsletter-issues.test.ts 20/20, CLI test/work.test.mjs 59/59
(including "newsletter metrics and sponsor report"), frontend
test/planning-surface.test.mjs 31/31 (one test added by a later intake).
Live prod checks (read-only, dataops CLI, 2026-11-05):
dataops newsletter list --from 2026-10-01 --to 2026-11-05 --json: issues #297/#298/#300 carry issue-level stats (e.g. #297: sent 130,878 · opens 40,719 · 31.11% · 831 clicks / 634 unique) and both slots carry per-slot stats with LinkedIn/X post URLs. #301 (sent 2026-11-02) has none yet — stats are recorded after send, as designed.dataops newsletter metrics 298(read form, GET only) prints the Mailchimp line and per-slot LinkedIn/X lines exactly as AC2 specifies.dataops newsletter report --sponsor Astronomer --from 2026-10-01 --to 2026-11-05returns per-sponsor reports across #298/#300/#301 — per-sponsor performance over time works live (AC4).- Card for #300 (
03870f8a): collect-mailchimp-stats / collect-x-stats / collect-linkedin-stats all done, Mailchimp task externalStatus proof reads "Mailchimp stats recorded in DataOps: 40445 opens (30.82% open rate), 620 link clicks (475 unique)" (AC5); "Send the performance of the newsletter to the sponsor" done; the retired "Add newsletter performance on the spreadsheet" task is skipped — the tracking spreadsheet is replaced in practice, not only in code.
Aggregate/comparison view (AC2 gap check): covered by existing views over the
records — portal Planning → Newsletter → Past renders the per-issue Mailchimp
stats line on every past issue since 2000-01-01
(frontend/src/surfaces/planning-newsletter.js past tab + issueStatsSummaryMarkup
in planning-newsletter-slots.js), i.e. open rate over issues;
dataops newsletter report --sponsor covers per-sponsor over time. No new
storage or view was needed.
CSV: the CLI produces no spreadsheet-shaped export, and none is required —
dataops newsletter sync <newsletter.csv> only consumes the retired schedule
sheet's CSV for the one-time migration (prod issues carry sourceType: "xlsx-import"); ongoing metrics live as structured records on the issues.