Status: scaffolded + offline-ready. Learns once the GA4/Blotato outcome signal is live.
The studio is already self-consistent (one source → all lenses, 13 gates fail on drift) and
self-correcting offline (the 5× audit→simulate→enhance convergence loop). This adds the missing
half: learning from the world — measuring what published content actually does and rewriting the
plan's own inputs so the next cycle is better. Pattern borrowed from karpathy/autoresearch
(fixed harness + one honest metric + keep/discard + evolving meta-instructions) and the production
patterns in aiming-lab/AutoResearchClaw (a lessons store that feeds the next run; PROCEED/REFINE/
PIVOT; an anti-fabrication guard — our no-faked-receipts rail). (turboquant is unrelated — KV-cache
quantization, no feedback loop.)
content-index.json (the assets) outcomes.json (REAL GA4/UTM + Blotato metrics)
│ │
└──────────────┬───────────────────────────┘
▼
engine/lib/improve.py · ONE round
1 SCORE every published asset → one comparable number (the metric)
2 RANK by score, within a lever (pillar · colorway · template family · hook)
3 DECIDE top-decile → SCALE · bottom-decile → KILL · middle → keep (PROCEED/REFINE/PIVOT)
4 PROPOSE write-back proposals to the SOURCE levers — never auto-applied:
· topics.yaml importance weights · ground/colorway rotation
· template-family preference · hook patterns that landed
5 LEARN append to lessons.json (time-decayed) — injected as context next round
│
▼
content/_generated/improvement-plan.json (operator reviews → applies)
content/_generated/lessons.json (the memory that compounds)
engine/lib/outcomes.py defines the per-asset OUTCOME schema (impressions · engagement_rate ·
clicks · leads · revenue · score). The score is a weighted composite, but the inputs are REAL
— read from content/_generated/outcomes.json (filled by GA4 via utm.py + Blotato post metrics).
**Offline (today): the file is empty → 0 outcomes → the loop runs, proposes NOTHING, and reports
"awaiting signal." It never invents a number** (the VerifiedRegistry / no-faked-receipts rail).
topics.yaml/treatment-plan is a separate, explicit, operator-gated step (improve.py apply --confirm). No silent self-editing of the plan.
families, the 8 pillars, sanctioned hooks) — the loop can reweight, never invent off-brand.
the same inputs yields the same plan. Gated alongside the 13 gates.
engine/lib/outcomes.py — the OUTCOME schema + load_outcomes() + score() + has_signal() (graceful-empty).engine/lib/improve.py — the round: score → rank → decide → propose → learn. CLI: python engine/lib/improve.py.content/_generated/outcomes.json — the REAL signal (GA4/Blotato fill it; empty offline).content/_generated/improvement-plan.json — the proposals (operator reviews).content/_generated/lessons.json — the compounding memory (time-decayed).python engine/lib/improve.py → "0 outcomes · awaiting GA4 signal · loop verified ready", writes an improvement-plan.json with status: awaiting-signal + 0 proposals, 0 fabricated numbers.
gcloud auth + GA4): outcomes.json fills → the round scores 304 assets, proposes scale/kill by lever, appends lessons; operator reviews improvement-plan.json and runs apply --confirm.
gcloud auth login (the GA4 property) — the outcome signal. (Operator action — only they can.)engine/integrations/ga4.py + Blotato post-metrics → write outcomes.json on a cadence.improve.py (cron, monthly — tied to the Receipts thread) → propose → operator applies.