Babanuj · Paid Ads Orchestration

Holistic paid-ads agent — build plan & live progress

An agent-first CLI that manages Google Ads, Amazon, Meta, and TikTok against one strategy — optimizing the whole portfolio, not any single platform. Built by a code factory: Claude plans and verifies, Codex implements, a second Claude reviews, Moe merges.

15Issues filed
0Coders running
1PR ready — Moe
5Merged

Plan & Progress

Phase 1 is a complete vertical slice on Google Ads in dry-run mode: strategy in → proposed actions out → experiments scored → lineage recorded, all runnable in CI with zero spend risk. Live mode is a flag flip afterwards. Every card maps to one GitHub issue, one branch, one PR.

ChiefClaude Fable 5 · high

Architecture, issues, dispatch, evidence verification. Writes no product code.

CoderCodex gpt-5.6-sol · medium → high on retry

All implementation, tests, verify runs, PRs. One disposable worker per issue.

ReviewerClaude subagent

Reviews each PR at its exact head SHA. Different model family than the Coder, by design.

Merge authorityMoe — only Moe

Nothing lands on main without human approval. READY PRs queue for sign-off.

Phase 1 — Google Ads vertical slice (dry-run)
#1 Scaffold TypeScript CLI + wire make verify — PR #9 Merged Landed on main as f7dfaa3 · CI pass, verify exit 0 twice, Reviewer APPROVE · follow-up hygiene in #10
#2 Strategy rules: schema, parser, strategy compile Merged Landed on main as 5da10a7 after review → drift-bug fix → rebase → byte-level re-audit · verify now fails on stale locks, proven twice
#3 Decision lineage store (append-only event log) Merged Landed on main as 90cc7d4 · Reviewer APPROVE after exercising the CLI against a real store · hardening notes → issue #13
#4 External signal contract + file inbox Merged Landed on main as 2266cfa · feature + post-rebase APPROVE, byte-level audit clean · hardening notes → #15
#5 Channel agent interface + Google Ads adapter (fixtures) Merged Landed on main as d9067f5 · Reviewer recomputed the decision arithmetic from raw fixtures; determinism + propose-only proven · follow-ups → #17
#6 Bounded experiments: isolation + budget guard Ready to merge PR #18 @ 8d39795 · APPROVE — Reviewer recomputed the 5% cap from all 300 fixture rows and proved the guard unbypassable via call-graph analysis · built by gpt-5.6-sol · follow-ups → #19
#7 Strategy-diff evaluator + confidence scoring Queued Beta-posterior per hypothesis · ≥ 0.80 across ≥ 3 cycles → draft rule for human review · depends on #2, #3, #6
#8 Orchestrator loop: daily + weekly ticks (end-to-end) Queued Ingest → compile → plan → guard → lineage · drift detection v0 · e2e fixture test in CI · depends on all above
Phase 2 — after the slice proves out
Live read-only mode via Babanuj CLIs · Meta & Amazon adapters · adstock/MTA feedback · cross-channel hypothesis engine · generative UI · incrementality cadence · TikTok Not yet filed Issues get filed as Phase 1 lands; live-mode wiring targets the plumbing below.

Existing plumbing (Babanuj)

Recon of ~/Babanuj and ~/babanuj-ops confirms the spec's claim: connectivity is already built. These CLIs (Go binaries in ~/go/bin) become the live data sources behind the ChannelDataSource port when we flip off dry-run.

ChannelCLIStatus
Google Adsgoogle-ads-pp-cliLIVE — OAuth2, Basic access, 4 accounts
Amazon Seller (SP-API)amazon-seller-pp-cliLIVE — store “Babanuj”, US/CA/MX/BR
Amazon Adsamazon-ads-pp-cliPending — API access submitted, refresh token outstanding
Search Consolegoogle-search-console-pp-cliLIVE — feeds the SEO-signal contract
Google Analyticsgoogle-analytics-pp-cliConfigured
Meta~/.config/meta-adsConfig present — CLI surface to be confirmed

Architecture

Canonical copy lives in the repo at docs/ARCHITECTURE.md; this is the same content, condensed. Three foundational decisions (Moe, Jul 6): TypeScript core, Google Ads first, dry-run on fixtures for v1.

1 · Strategy is Markdown rules, compiled

One rule per file under strategy/rules/ — YAML frontmatter (id, status, scope, channels, metric, confidence, provenance, approved_by) over a human-readable rationale. fable strategy compile validates everything against a JSON Schema and emits one canonical strategy.lock.json that channel agents consume.

Rules live in git: an agent-proposed change is a PR; human approval is the merge. The approve-loop and its audit trail come free, and an invalid strategy fails make verify.

2 · Experiments are ring-fenced by the framework, not the channels

An abstract experiment carries a hypothesis, a pre-registered metric and direction, a window, and an isolation request. Channel adapters declare which isolation modes their platform supports (budget-slice, geo-split, audience-split) and translate. A central budget guard rejects anything over the cap (default 5% of channel spend) — enforced above channel code so no adapter can bypass it.

3 · Confidence is a Beta-posterior over validation cycles

A validation cycle is one completed experiment window (default 7 days) scored against the strategy-diff baseline: did the portfolio move toward the strategy, relative to baseline. When P(effect > 0) ≥ 0.80 across ≥ 3 cycles, the hypothesis becomes a draft rule with provenance links — never an automatic change.

Deliberately simple and auditable; the model gets smarter only when lineage data proves it's miscalibrating.

4 · External agents speak versioned JSON events

SEO, creative, and website-change signals arrive as CloudEvents-style JSON validated against schemas in contracts/, through a file inbox the orchestrator polls. Transport can become a queue later without touching consumers. Every ingested signal gets a lineage entry so attribution can cite it.

5 · Attribution ships lineage-first

v1: tactics/strategy separation + decision lineage + strategy-diff evaluation, no causal claims. v2: adstock / multi-touch attribution from GA. v3: periodic geo-holdout incrementality tests.

The later layers are unauditable without lineage, so lineage ships first.

6 · One orchestrator, stateless channel agents

A single orchestrator owns budget guards, cross-channel gating, and lineage writes. Channel agents are stateless per tick: read strategy.lock.json + channel data, emit proposed actions. Cadence: daily tactical tick, weekly experiment-evaluation tick.

Product invariant — cross-channel gating. Cross-channel signals are hypothesis-only until they earn ≥ 0.80 confidence across ≥ 3 cycles. They never silently alter per-channel strategy; they enter it only as human-approved rule changes with provenance. This is a hard constraint, not a tuning default.

Learnings

Mirror of docs/LEARNINGS.md — the log that makes disposable agents cumulative. Every regression, failed review, or workflow failure becomes an entry; every agent reads the log in full before working; proven rules get promoted into AGENTS.md.

2026-07-07 · Three headless-Codex failure modes, found and fixed in sequence

Symptom: the first Coder sat ~18 hours with zero output, no branch, no PR. Two re-fires surfaced two more failures before the worker finally ran clean.

Root causes, peeled in order: (1) backgrounded codex exec inherits an open stdin pipe and waits forever for input — the primary hang; (2) approval_policy = "on-request" would deadlock any headless run at the first sandbox approval, since nobody exists to answer it; (3) the pinned model gpt-5.5-codex isn't available on this machine's ChatGPT-account Codex — the account runs gpt-5.5. Chief monitoring compounded #1 by piping worker output through tail, which buffers until exit — nothing to see mid-run.

New rules: headless workers launch with stdin closed (< /dev/null), approval_policy = "never", network enabled; logs stream unbuffered; a silent or non-growing log for 10–15 minutes means presumed-dead — kill, diagnose, re-fire; and smoke-test the exact dispatch configuration, not a simplified variant.

Outcome: fixes promoted into .codex/config.toml; Coder model is gpt-5.5 (high; xhigh on escalation) until the account gains codex-model access. Exact-config smoke test passes; worker re-fired with error + stall monitors.