Skip to content

Activation Plan

This page defines the exact ordered steps required to go from current state (dry-run validated on devlop) to production active (cron running on main with DRY_RUN=false every day at 8 AM Colombia).

Every step includes who owns it, what done looks like, and whether it is a blocker for the next step.


Current State Summary

Item Status
Core automation code ✅ Complete, on devlop branch
Offline tests (18/18) ✅ Passing
Dry-run against real APIs (local) ✅ Done 2026-04-15 — 35 states, 5 adsets
GitHub Secrets ⚠️ Not configured
IP whitelist for GitHub Actions ⚠️ Unresolved — main blocker
Mike confirmation of dry-run output ⚠️ Pending
devlopmain merge ⚠️ Pending
First live cron run ⚠️ Pending

Step 1 — Mike confirms the dry-run state list

Owner: Juanes | Blocker for: Step 4 (live run) | Status: ⚠️ Pending

Share the dry-run output with Mike and ask him to confirm that the 35 active states are correct:

AK, AR, AZ, CA, CO, CT, DE, FL, GA, HI, IA, IL, LA, MA, MD, ME, MN, MO, MS, MT, NC, ND, NH, NJ, NM, NV, OH, PA, RI, TN, TX, VA, WA, WI, WV

If Mike says these are wrong, there is a bug in how CLOSRTECH is being queried (wrong campaign ID, wrong credentials) and that must be fixed before any live run.

Done when: Mike replies confirming the list looks correct.


Step 2 — Resolve the IP whitelist

Owner: Juanes (decision) + Nat (implementation) | Blocker for: Steps 3 and 5 | Status: ⚠️ Unresolved

Choose one of the options documented in github-actions.md:

Option Recommended if...
Static IP proxy (VPS) Nheo doesn't have an existing server with a fixed IP
Self-hosted GitHub Actions runner Nheo already has a server that's always online
Move cron to Nheo server directly Team prefers no GitHub Actions dependency
Ask Mike to expand CLOSRTECH whitelist Mike has direct access to CLOSRTECH's admin and the vendor is responsive

Once decided, implement the chosen solution and verify that a curl or python call to demand.php from the target execution environment succeeds.

Done when: A test HTTP request to demand.php from the chosen runner/environment returns a valid JSON response (not 403).


Step 3 — Configure GitHub Secrets

Owner: Juanes or Nat | Blocker for: Step 5 (merge + cron) | Status: ⚠️ Not configured

In the GitHub repo: Settings → Secrets and variables → Actions → New repository secret.

Secret Source
CLOSRTECH_EMAIL Mike's CLOSRTECH credentials
CLOSRTECH_PASSWORD Mike's CLOSRTECH credentials
CLOSRTECH_CAMPAIGN VND_VETERAN_LEADS
FB_ACCESS_TOKEN Meta Business Manager → System User token
FB_AD_ACCOUNT_ID From Facebook Ads Manager (format: act_XXXXXXXXXX)
FB_CAMPAIGN_ID Campaign ID for VND_VETERAN_LEADS campaign on Facebook
SLACK_WEBHOOK_URL Optional. Create an incoming webhook in Slack if notifications are wanted

Important: Do not commit any of these values to the repo. Verify that .gitignore includes .env.

Done when: All secrets appear in the GitHub repo secrets list (values are masked, names are visible).


Step 4 — Manual workflow_dispatch dry-run from GitHub Actions

Owner: Nat | Blocker for: Step 5 (merge) | Status: ⚠️ Pending Steps 2 and 3

Once the IP issue is resolved and secrets are configured, trigger the workflow manually from GitHub Actions UI with dry_run: true.

What to verify in the run log: - No auth errors from CLOSRTECH (IP whitelist resolved) - No auth errors from Facebook (token valid) - Same 35 states returned as in the local dry-run - Same 5 adsets found - Log shows [DRY RUN] Would update adset... for each adset - Exit code 0 (green check in GitHub Actions)

If the run fails, diagnose from the workflow log. Common failure points at this step: CLOSRTECH still rejects the IP, Facebook token permissions issue, or a secret name typo.

Done when: A workflow_dispatch dry-run from GitHub Actions completes with exit code 0 and the expected output.


Step 5 — Merge devlopmain

Owner: Juanes | Blocker for: Step 6 (first live cron) | Status: ⚠️ Pending Step 4

Create a PR from devlop to main. Review the diff — it should be the entire CLOSRADS codebase since main is currently empty or stale.

Merge only after Step 4 passes. The cron workflow is configured to run from main, so this merge is what arms the scheduled trigger.

Done when: PR merged, main contains all current code.


Step 6 — First live cron run (DRY_RUN=false)

Owner: Nat (monitor), Juanes (sign-off) | Blocker for: Nothing — this is the finish line | Status: ⚠️ Pending Step 5

The day after the merge to main, the cron fires at 13:00 UTC (8 AM Colombia) with DRY_RUN=false.

What success looks like: - Exit code 0 - Log shows actual Facebook API update calls (not dry-run logs) - Slack notification shows Status: SUCCESS with adsets updated count - Mike's Facebook Ads Manager shows the geographic targeting updated on the 5 adsets

What to do if it fails: - Check the GitHub Actions log for the exact error and which step failed - If CLOSRTECH fails: check IP whitelist, credentials - If Facebook fails: check token expiry, permissions, adset IDs - If sync logic fails: check report.errors in the Slack message or log - Do NOT attempt a manual fix in Facebook Ads Manager simultaneously — let the automation own the targeting

Done when: First live run completes with exit code 0 and Facebook targeting is confirmed updated.


Blockers Summary

Blocker Severity Owner Next action
IP whitelist for GitHub Actions 🔴 Critical Juanes Decide on proxy vs self-hosted runner vs server cron
Mike confirmation of dry-run states 🟡 High Juanes Share output with Mike and await reply
GitHub Secrets not configured 🟡 High Nat Configure after IP decision is made
devlopmain not merged 🟡 High Juanes Merge after Step 4 (GH Actions dry-run) passes
orders.php returns 404 🔵 Low (v2) Mike Mike must escalate to CLOSRTECH dev. Not blocking v1

Post-Activation Monitoring

Once live, the automation should be monitored for the first 5 days: - Check GitHub Actions workflow history each morning to confirm the run succeeded - Spot-check Facebook Ads Manager on Day 1 and Day 3 to confirm targeting matches CLOSRTECH - Confirm Slack notifications are being received (if configured) - After 5 successful days, consider the automation stable and reduce monitoring to weekly spot-checks

Long-term: The System User token does not expire, so no credential rotation is expected. The only maintenance scenario is if Meta changes the facebook-business SDK (update requirements.txt) or if CLOSRTECH changes their API contract (update closrtech_client.py).