Commit graph

7 commits

Author SHA1 Message Date
c2f036ab21 feat: Plan C — BlogPostStrategy and migration 017
Adds blog_post campaign type that publishes directly to the CircuitForge
website via Directus CMS. Implements PostingStrategy ABC with:
- supports_dupe_guard() → False (blog posts run on demand, no weekly guard)
- execute() → wraps publish_blog_post() from directus.py
- tags stored as JSON string in DB, decoded at post time

Migration 017 adds slug, tags, seo_description columns to campaign_variants.
Poster merges variant blog fields into extra dict before strategy execution.
Seed script updated with blog campaign (id=9, target=blog/main).
Registry updated; 6 new unit tests added.
2026-05-25 14:43:43 -07:00
a2620570fa feat(dupe-guard): add max_posts per-sub cap to prevent one-shot intro campaigns from re-posting
Adds max_posts INTEGER to campaign_subs (NULL = unlimited/evergreen).
Adds successful_post_count() query counting lifetime success records.
poster.py checks max_posts before the 7-day rolling dupe guard.

Root cause: campaign 2 fired 8 days after the last post (just outside the
7-day window), allowing a duplicate r/opensource pitch. Fix: set max_posts=1
on intro campaigns so the lifetime cap fires regardless of window.
2026-05-06 08:52:21 -07:00
774fbb37c3 feat: pass variant blog fields (slug, tags, seo_description) to strategy extra dict 2026-04-27 16:11:04 -07:00
a3932aef1e fix: handle ValueError from parse_occurrence and add edge-case occurrence tests
- Wrap parse_occurrence() call in try/except ValueError; return skipped with reason instead of crashing
- Remove redundant `or {}` guard on sub_row (already defaulted to {} via next(..., {}))
- Strengthen test_occurrence_passes assertion to check status == "success"
- Add 3 edge-case tests: occurrence="every", missing occurrence key, invalid occurrence string
2026-04-27 12:57:44 -07:00
08aa019439 feat: add occurrence check to poster before strategy dispatch
Parse the occurrence field from sub_row and skip execution when today
is not the nth weekday specified (e.g. first_sunday). Check runs after
sub_row fetch but before dupe guard. Two new tests confirm skip and
pass paths using patched date.today in app.services.poster.
2026-04-27 12:27:16 -07:00
81a63ab0ec refactor: dispatch poster by campaign.type via platform strategy registry
Replace hardcoded platform dispatch with get_client(campaign["type"]) so
any future campaign type (blog_post, email, etc.) routes automatically
through the strategy registry. Adds dupe-guard opt-out per strategy,
sub_row pre-fetch for extra metadata, and 5 new TDD tests (14 total).
2026-04-27 08:38:12 -07:00
2cc85d8fc5 feat: scaffold Magpie — campaign scheduler + social posting platform
FastAPI backend (SQLite + APScheduler), Vue 3 frontend, MCP server for
Claude integration, and Docker Compose stack. Includes campaign data model
(campaigns → variants → subs), post history, sub rules, and Playwright-based
Reddit posting layer migrated from claude-bridge/reddit-poster.

Also seeds legacy campaigns (6) and sub rules (14) from reddit-poster history.

Closes #1 (scaffold), resolves migration from claude-bridge.
2026-04-21 16:51:33 -07:00