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.
Phase 1 is complete. The full loop runs in dry-run with zero spend
risk: fable run --tick daily ingests signals, compiles strategy, proposes and
dry-executes Google actions under the budget guard, and flags strategy drift;
--tick weekly evaluates closed experiment windows, accumulates Beta-posterior
confidence, and emits draft rules for human review. Every step lineage-logged; the whole loop
is an e2e test inside make verify. Next: hygiene batch, then Phase 2 (live
read-only against the Babanuj CLIs).
Architecture, issues, dispatch, evidence verification. Writes no product code.
All implementation, tests, verify runs, PRs. One disposable worker per issue.
Reviews each PR at its exact head SHA. Different model family than the Coder, by design.
Merges fire automatically once every gate is green: CI + DCO + APPROVE review, all on the exact head SHA. Any red gate blocks.
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.
| Channel | CLI | Status |
|---|---|---|
| Google Ads | google-ads-pp-cli | LIVE — OAuth2, Basic access, 4 accounts |
| Amazon Seller (SP-API) | amazon-seller-pp-cli | LIVE — store “Babanuj”, US/CA/MX/BR |
| Amazon Ads | amazon-ads-pp-cli | Pending — API access submitted, refresh token outstanding |
| Search Console | google-search-console-pp-cli | LIVE — feeds the SEO-signal contract |
| Google Analytics | google-analytics-pp-cli | Configured |
| Meta | ~/.config/meta-ads | Config present — CLI surface to be confirmed |
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.
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.
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.
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.
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.
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.
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.
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.
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.