2 Commits
Author SHA1 Message Date
AFFAANhandClaude Opus 5 9dba7b81d4 fix(campaigns): launch the whole object chain, not just the campaign
Launch set only the campaign to ACTIVE. Sync creates the ad set and ad
PAUSED, and Meta delivers only when the ad, its ad set and its campaign
are all active — so Launch reported "live" while nothing ran. This was
the central promise of Phase 2 and it did not work.

The chain is now activated children-first, campaign last. Nothing under a
paused campaign delivers, so a failure part-way leaves the campaign unable
to spend. That ordering is also why pause and stop only flip the campaign.

An imported campaign has no stored ad set or ad ids, so launching it still
touches only the campaign and its children keep the statuses set in Ads
Manager.

Deleting an imported campaign is now refused rather than silently
pointless: deleting it on Meta would destroy work MaskanX did not author,
and deleting only the local row achieved nothing because the reconciler
re-imported it on the next cycle. Deleting it in Ads Manager is what
sticks, after which the reconciler archives the record.

Also corrects the README's reconciler interval: it is 120s, not 300s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 17:45:07 +05:30
AFFAANhandClaude Opus 5 8153edfce9 feat(campaigns): add launch, pause and stop
Launch is the only action that starts real spend, so it refuses unless every
precondition holds: the campaign is synced, its status allows launching, an
identified operator approved it, and the ad account has a payment method.
Each refusal names what to do rather than letting Meta fail opaquely later.

An approval recorded as "unauthenticated" does not authorise spend. With
MASKANX_OPERATOR_TOKENS unset every approval is unattributable, so launch is
blocked until operator auth is configured.

Stop and pause set the Meta object PAUSED before recording the local change,
so a Meta failure cannot leave a campaign that is stopped in MaskanX but
still delivering.

Replaces the Phase 1 test asserting /launch did not exist with one asserting
it is unreachable from draft; the guarded property is unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 11:37:35 +05:30