Know how your buyers will react before you spend a dollar.

Paste your draft ad into one command: a demographically weighted panel of 20 synthetic buyers reads it in character and returns a population-level favorability verdict, corrected by the measured error of your past campaigns.

Test my first ad free

Free on your own machine, forever — no credit card, no API key required. · Pricing

20personas, in character
5localities with live context
~500Kmodeled buyers, stratum-weighted
$0per read on the local route
28signed axioms govern the design

Built for founders of small, fast-launching brands

We built the focus group we couldn't afford.

OVK started the way most small brands do: prescription glasses from $29, a monthly drop cadence, and a founder writing ad copy at midnight. Every headline was a guess. A real focus group meant weeks of lead time and an agency invoice — for a brand shipping a new collection every month, the answer would arrive after the campaign it was meant to save.

A/B testing wasn't the fix either. It tells you which of two guesses lost less, and it bills you for both. What we wanted was the thing agencies sell: sit twenty real buyers in a room, read them the ad, watch their faces. Just — tonight, and again tomorrow, and free.

So we built the room. Twenty personas, each assembled in three layers: real demographic strata drive who is in the room and how much their voice counts, a Jungian archetype gives each one instincts and a way of speaking, and a locality bundle — this week's weather, the local paper's front page — puts something on their mind before your ad ever reaches them.

Now every draft goes through the panel before it goes anywhere else. One command returns 20 in-character reactions and a favorability score weighted to a ~500K modeled population, broken down by archetype and locality — in about a minute, for $0 on the default local model. And when the real click-through comes back, we feed it to the same queue: the panel measures its own error and corrects its next prediction by exactly that much.

How it works

One command in. One calibrated verdict out.

Never ship a blind guess again.

Your draft fans out to all twenty personas at once and comes back as a single weighted prediction, with the disagreements — by archetype, by locality — laid out underneath it.

Feature: panel-run.sh appends your copy as content:query, collects all replies in one atomic batch, and aggregates them with per-stratum population weights (panel/weights.tsv).

Reactions from people, not templates.

Frank in Rochdale reads your ad differently than Chloe in Austin — because he's a sceptic in a frugal town where it rained all week, and she's a trend-forward optimist in SXSW season.

Feature: each persona is three layers — census-style demographics for counts, one of 12 archetype voices for character, and a shared per-locality context bundle (panel/panel.json).

It gets sharper with every campaign.

Append the real outcome — CTR, conversions — and the panel joins it to its own prior prediction, measures how far off it was per stratum, and subtracts that bias from the next verdict.

Feature: outcome-append.rbcalibrate.rb builds the per-stratum error table; PredictionCorrector applies it, flagging every corrected number with the history that justified it.

A verdict that admits what it doesn't know.

Predictions are labeled directional until enough real cycles accrue; uncalibrated strata pass through unadjusted and say so. You always see the honest unweighted number next to the weighted one.

Feature: every claim rides one append-only, immutable queue — queries, replies, predictions, outcomes — so any number on any report can be replayed from source (AX-OVK-00001, 00017, 00019).

Features

Everything in the box, and what each piece is for.

All-or-nothing fan-out

One orchestrator walks the queue with a single durable cursor and lands all 20 replies as one atomic batch — a run never half-completes.

persona-orchestrator.sh · AX-00012

Route any task to any model

Every model call goes through one seam that maps each role — reply, classify — to a free local SLM, Claude, or mock. Change a config line, not a script.

llm.sh · panel/llm.env · AX-00024

Deterministic mock mode

The entire loop — replies, scoring, aggregation, calibration — runs with zero model calls, so you can prove and test the plumbing for free.

REPLY_ENGINE=mock · SENTIMENT_ENGINE=heuristic

Weighted, with the honest number beside it

Population estimates come from stratum weights, never naive multiplication — and every report shows the raw unweighted figure for comparison.

panel-aggregate.sh · weights.tsv · AX-00016

Live persona dashboard

A long-running terminal view groups replies per persona as they arrive, so you can watch the room react in real time.

persona-dashboard.sh · AX-00010

Replayable audit trail

Queries, replies, predictions, and outcomes live on one append-only, immutable queue — any number on any report can be recomputed from source.

ground flow · AX-00001 · AX-00019
Test my first ad free

No credit card, no API key required. · Pricing

Roadmap

Exactly where it stands — shipped, working, and still open.

Every open item below is a signed unknown on the project's axiom chain, not a hidden caveat.

ARRIVALSSHIPPED · LANDED & IN FLIGHT
DateFlightDeliveryStatus
Jul 14AX-00001Append-only flow queue — single source of truthLanded
Jul 14AX-00010Live persona dashboardLanded
Jul 15AX-00012Single-orchestrator atomic fan-outLanded
Jul 20AX-00016Three-layer panel · 20 personas, stratum-weightedLanded
Jul 20AX-00024Model seam llm.sh · local / Claude / mockLanded
Jul 20AX-00018Calibration loop closed · error table appliedLanded
ETA TBDOVK-001First real campaign outcomes — calibration cycles accrueIn Flight
ETA TBDAX-00020Census source & load-bearing strata for eyewear buyersIn Flight
ETA TBDAX-00021Archetype framework for the voice layerIn Flight
ETA TBDAX-00022Cycles needed before error signature beats outcome noiseIn Flight
ETA TBDAX-00023Attribution model — net out spend, seasonality, platformIn Flight
ETA TBDAX-00028Per-persona pools & filter predicates — decided, unbuiltIn Flight

Working, earning trust

  • End-to-end panel runs: query → 20 replies → weighted prediction, in mock and live-model modes
  • History corrections apply only where real outcome data exists; untouched strata are flagged, never silently adjusted
  • All predictions labeled directional until enough calibration cycles accrue AX-00017

Glossary

Every OVK term, in one place.

The bus

Rapids AX-00026
The message bus itself: one shared, immutable queue every message enters — owned by no one, addressed to no one.
Stream
A listener taking messages off the rapids through a named cursor — the act of reading, not a place.
Pool AX-00027
The messages a listener retains for later context, held as references (queue positions) into the rapids, never copies.
Flow queue AX-00001
The ground flow daemon behind the rapids: entries are appended once and never modified, deleted, or consumed.
Cursor
A named, durable position marker on the queue — the only record of a reader's progress.
Atomic append-batch
A group of entries (e.g. all 20 replies) written so they land all-or-nothing; a run never half-completes.
Orchestrator AX-00012
The single live responder: one durable cursor, fan-out to every persona, one atomic reply batch per inbound message.
Fan-out
One inbound message becoming one in-character reply from every persona on the panel.

The panel

Persona AX-00013
A synthetic buyer assembled in three layers: demographics for counts, an archetype for voice, a locality for what's on their mind.
Panel AX-00016
The active 20 personas — a proportionally stratified sample of a modeled ~500K buyer population.
Stratum & weight
A demographic slice's share of the modeled population; it decides how much that persona's voice counts in aggregation.
Archetype AX-00014
The Jungian character (Everyman, Caregiver, Explorer…) shaping a persona's voice and lens — never used to slice counts.
Locality bundle AX-00015
One shared package of local context — weather, news, vibe — fetched once per place and shared by everyone sampled there.

Messages & verdicts

content:query
A draft ad or question put to the panel.
reply:<persona>
One persona's in-character reaction, tagged with their name.
prediction:<item>
The panel's weighted favorability verdict for a content item.
outcome:<item>
The measured real-world result — CTR, conversion, engagement — joined back to the prediction it tests.
Favorability
The 0–1 score aggregated from replies; always reported weighted (population) with the raw unweighted number beside it.
Directional AX-00017
The honesty label every prediction carries until enough real calibration cycles accrue.

Calibration & machinery

Calibration loop AX-00018
Join each outcome to its prediction, build the per-stratum error table, and subtract the measured bias from the next verdict.
Calibration cycle
One full pass: a prediction, its real outcome, and the joined error measurement.
Error table & mean_error
Per-stratum/per-archetype average of (predicted − actual); positive means the panel over-predicted.
Corrected from history
The flag on any number adjusted by measured error; strata without history pass through untouched and never carry it.
Model seam AX-00024
llm.sh, the only place a model is ever called; routes each task to a free local SLM, Claude, or mock.
Mock engine
Deterministic, model-free replies and scoring — proves the whole loop for $0.
Axiom (AX-OVK-…)
A signed block on the project's append-only ground-truth chain: truths, decisions, supersessions, and open unknowns.
OVK
The eyewear brand the panel was built for — designer-look prescription glasses from $29 — and this project's name.

Before you run it

Questions founders actually ask.

Do I need an API key or a paid model?

No. The default routes are a deterministic mock engine and any local OpenAI-compatible model; Claude is an optional per-task route you turn on in panel/llm.env only if you want it.

What does it cost to run?

$0 on the mock and local-model routes — the software is yours and runs entirely on your machine. If you point a task at a hosted model like Claude, you pay that provider's per-call price and nothing else.

Is a synthetic panel actually predictive?

Out of the box it's directional, not survey-grade — LLM personas drift toward the modal opinion, and the system says so on every prediction. Defensibility is earned by the calibration loop: real outcomes measure the panel's bias per stratum and the correction is applied automatically.

Can I swap in my own market and personas?

Yes — personas, localities, and population weights are plain data in panel/panel.json. Edit them and run panel-generate.sh to rebuild the panel.

Where does my draft copy and campaign data go?

Onto a local append-only queue on your own machine, and nowhere else. Nothing leaves your box unless you deliberately route a task to a hosted model.

Your next ad deserves a read before it ships.

# from the repo root — mock engine, no model needed ./scripts/panel-run.sh "Your draft ad copy here"
Command copied — paste it in your terminal.

Free on your own machine, forever — no credit card, no API key required. · Pricing