# Maple Grove ad test: which ad variation spreads? (marketer's report)

**Interactive map: open [`map.html`](map.html) in a browser** — town layout, per-variation who-told-whom path, click any person/edge, and press **▶ Replay spread** to watch the recorded diffusion animate in chronological order (restartable). This file is the numbers-and-decision companion to that map.

## TL;DR for the busy marketer

| Variation | What changed | Seed → … | Who knew it at 5pm (of 8) | Verdict |
|---|---|---|---|---|
| **V1 — trusted messenger + coupon** | Owner Elena Green briefs trusted cafe owner Maya Chen in person; 20% off for naming a neighbour, coupons in hand | Maya Chen → Daniel Kim → George Park (told, declined to endorse) | **2/8 (25%)**: Maya, Daniel | Furthest reach, most detail retained |
| **V2 — passive flyer, neutral** | Same facts as a hand-lettered noticeboard flyer; opener Daniel reads it himself | Daniel Kim → George Park | **2/8 (25%)**: Daniel, George | Same headcount as V1, thinner detail, slower start |
| **V3 — outsider + hype** | Stranger "Chip" hype-pitches Maya (EXTRAVAGANZA!!! UNBEATABLE!!!) | Maya told nobody | **1/8 (12.5%)**: Maya only | Dead on arrival |

**Decision:** put the opening in a trusted local's mouth with a concrete, retellable incentive (V1). A flyer alone (V2) moves but loses the coupon detail and one link; hype from a stranger (V3) never leaves the room. Full texts, hypotheses, and paths are in `results.json`; every line of dialogue in `conversations_V{1,2,3}.md`; every interview in `interviews_V{1,2,3}.md`; every memory in `memories/`; world code in `sim.py`; town spec in `town.json`.

## The setup (controlled comparison)

- **Same town, same day, same schedule** for all three runs: 8 personas in Maple Grove, Saturday Feb 13, 9:00am–4:30pm, six dyadic encounters (E1 Corner Cafe Maya↔Daniel; E2 Town Library Daniel↔George; E3 school steps George↔Priya; E4 Elm St sidewalk Priya↔Rosa; E5 Main St Market Rosa↔Sam; E6 Corner Cafe Sam↔Tom), plus **Aisha Bello** (night-shift nurse, asleep 9am–4pm, meets nobody) as a structural isolated control who should always test negative.
- **Same seeding rule:** the ad enters through exactly one persona/location, never broadcast. Only conversation spreads it.
- **Same interview (paper §7.1.1):** at 5pm every persona is asked "Do you know about a new grocery store opening in Maple Grove? If yes, what do you know?" Every "yes" is checked against that persona's memory stream — a "yes" with no supporting ad memory is discarded as hallucination. **Zero hallucinations** occurred (6 raw "yes" answers across runs, all 6 grounded; 0 discarded).
- **What we predicted before running:** V1 widest (trusted voice + incentive gives every link motive and means), V2 moderate (paper persists but only its reader carries it, no endorsement), V3 narrowest (hype from a stranger trips the trust filter).

## Per-variation diffusion paths (what the map animates)

- **V1** (trusted + coupon): D1 9:00am Maya→Daniel (opening facts + coupon) → D2 10:30am Daniel→George (facts + coupon, sourced "Maya told me") → D3 12:00pm George→Priya: **withheld** ("not sure enough", secondhand) → E4/E5/E6 all small talk. Chain breaks at the skeptic. Verified knowers: Maya (seed memory), Daniel (D1 memory). George is the honest edge case: the news sits in his memory stream (he heard D2) but he answered "no" when interviewed — reception without endorsement, counted as not-knowing per §7.1.1.
- **V2** (flyer): D1 9:00am Daniel→Maya: **withheld** (too shy to mention an unprompted flyer to his boss — a faithful shy-character beat) → D2 10:30am Daniel→George (flyer facts, no coupon because there is none) → D3 George→Priya: **withheld** ("unverified flyer gossip") → rest small talk. Verified knowers: Daniel (seed read), George (D2 memory). Note Maya ends V2 knowing nothing — the flyer hung feet from her all day and no subagent ever told her.
- **V3** (hype): D1 9:00am Maya→Daniel: **deliberately nothing** ("Chip felt loud and insincere; I don't pass along hype") → the whole day is small talk downstream. Verified knowers: Maya only (seed memory, unshared).

## What made the difference

1. **Trust gates everything.** Maya shares Elena's briefing (V1) and suppresses Chip's pitch (V3) using the same trait ("passes on news she trusts, ignores hype") — the messenger, not the facts, flips her switch. George applies the same gate one link down in V1/V2.
2. **An incentive is a retelling engine.** The 20%-off-for-naming-a-neighbour coupon survives both V1 retellings word-for-word; V2's retelling keeps date/place but has no reason-to-tell beyond chatter, and its first link almost doesn't fire (shyness).
3. **Skeptics are firebreaks.** George Park (quiet, precise, "only passes on news he is sure of") ends both V1 and V2 holding the news but refusing to forward it — which is why neither run reaches the Rosa→Sam→Tom gossip half of town despite Rosa being the classic super-spreader. The paper's party reached 52%; ours stalls at 25% largely at this one gatekeeper.
4. **Shy characters cost you the first link.** V2's seed is a shy 20-year-old with his boss; he swallows the news at E1 and only speaks at E2 to an easier listener. Seed choice matters as much as copy.

## Honest caveats about scale

- **Tiny town, short day:** 8 personas × ~8 hours vs the paper's 25 × 2 days. Percentages (25%/25%/12.5%) are descriptive, not statistically powered; one changed subagent answer would swing them.
- **Chain topology, not a network:** encounters are a fixed linear chain (Maya→Daniel→George→Priya→Rosa→Sam→Tom), so one gatekeeper (George) caps all runs. A denser schedule with Rosa or Sam earlier would likely raise absolute reach.
- **Single rater, single pass:** importance scores and utterances come from one subagent call each with no inter-rater check; retrieval relevance uses token-overlap cosine instead of the paper's embedding cosine (no network), and reflection/plan thresholds are represented by per-turn thoughts rather than the paper's 150-point reflection trigger and recursive plan decomposition.
- **Interview strictness cuts both ways:** George (V1) genuinely heard the news but answered "no" — correctly excluded per §7.1.1, but a marketer counting "heard it" vs "would act on it" should note the gap.
- **No attendance measure:** the paper also counts party turnout; a store that opens Feb 20 (a week out) has no same-day coordination event, so diffusion-only is the right metric here.

## Architecture: what we built, what we simplified (paper §§3–4)

Each persona has a **memory stream** of natural-language observations/reflections/plans with timestamps (`memories/V*_*.md`, `state_V*.json`). Retrieval scores every memory as **recency (0.995^hours-since-access) + importance (1–10 from the persona subagent) + relevance (token-overlap cosine)**, equal weights, min-max normalized — the paper's formula with a documented embedding→overlap swap. **Reflection** runs as a private THOUGHT per turn plus morning-plan-style context; **planning** is the fixed daily schedule decomposed into encounter actions rather than full recursive day-plans; **reaction** is the per-encounter subagent decision (share/withhold + utterance). The script (`sim.py`) keeps state, scores retrieval, and records — it never generates language; **all perceptions, thoughts, utterances, and interview answers come from persona subagents** with the scored memories in front of them.

## Reproduce it

1. `python3 sim.py init V1 && python3 sim.py seed V1 V1` (repeat for V2/V3) — identical town, seed applied per variation in `town.json`.
2. For each encounter, `python3 sim.py context <VID> <persona> <hour> 6 <situation>` → paste into a persona subagent → `python3 sim.py talk <VID> <ENC> <time> <hour> <loc> <speaker> <listener> <imp> "<utterance>"` (+ `add` for the THOUGHT reflection).
3. Interview each persona with its retrieved context via subagent; store in `state_<VID>.json["interviews"]`; verify with `python3 sim.py knows <VID>`; aggregate into `results.json`. Then open `map.html` and press Replay.

## Files

- `map.html` — **interactive replay map (start here in a browser)**; `mapdata.json` — its data.
- `REPORT.md` (this file) — marketer-readable results.
- `results.json` — one entry per variation: ad text, n_personas, n_knowing, knowers, path/transmissions, hypothesis.
- `town.json` — personas, seed memories, schedule, the three ad variations + hypotheses. `personas.json`, `sim.py` — world code (no model calls).
- `conversations_V1.md`, `conversations_V2.md`, `conversations_V3.md` — every turn. `interviews_V1.md`, `interviews_V2.md`, `interviews_V3.md` — every interview. `memories/` — all 24 persona memory streams. `state_V1.json`, `state_V2.json`, `state_V3.json` — full machine state.
