"""Writes spec/launch-campaign.json, the society spec of the launch-campaign study.

    python research/studies/market-perception/press-brain-ltv/make_spec.py

The spec is checked in; this is how it was made, so the panel can be changed and
regenerated rather than hand-edited. The 28 readers are drawn once here (Python's
random.Random(2026)): names without replacement, jobs round-robin within a cohort so every
job appears, the rest at random. Everything in it (Ferrovane, the VX-4, Plant Floor
Weekly, Bench Notes Lab, every person and post) is fictional and exists only in this
simulation.
"""
import pathlib
import json
TEMPER = ["skeptical and slow to share", "trusting and quick to share", "excitable", "cautious", "contrarian", "earnest and practical", "busy and terse"]
INTENT = ("You just picked up your phone and X is open on your home feed. Browse it and engage as you naturally "
          "would: like, repost, reply, post, follow accounts, open profiles or search, only if you genuinely want to. "
          "When you have done what you want for now, reply done.")
import random
RNG = random.Random(2026)
COHORT_IDS = {}
def pop(pid, count, names, jobs, backstory, interests):
    """Named people for one cohort, drawn once by this generator (seed 2026) and checked in:
    names without replacement, jobs round-robin so every job appears, the rest at random."""
    names = RNG.sample(names, count)
    people = []
    for i in range(count):
        first = names[i].split()[0]
        person_id = f"{pid}-{i}"
        age = max(22, min(68, int(RNG.gauss(41, 11))))
        people.append({
            "id": person_id, "cohort": pid,
            "devices": [{"machine": f"{person_id}-phone", "profile": RNG.choice(["ios", "ios", "ios", "android", "android"]), "apps": ["x-social"]}],
            "accounts": {"x-social": f"{first.lower()}{i}"},
            "persona": {"name": names[i], "title": jobs[i % len(jobs)], "interests": RNG.sample(interests, 2),
                        "temperament": RNG.choice(TEMPER), "backstory": f"{age} years old. " + backstory},
            "policy": {"kind": "llm", "intent": INTENT},
            "activation": {"propensity": "hours(17-20:0.6, else:0.4)"}})
    COHORT_IDS[pid] = [p["id"] for p in people]
    return people

PEOPLE = [
 pop("practitioners", 12,
     ["Marisol Vega", "Dwayne Pruitt", "Ingrid Solberg", "Hector Ruiz", "Tamsin Hale", "Kwame Asante", "Lena Kowalski", "Rory Gallagher", "Anjali Rao", "Curtis Boone", "Yolanda Price", "Ezra Whitlock"],
     ["reliability engineer at a food-packaging plant", "maintenance manager at a metal-stamping shop", "plant engineer at a regional brewery",
      "controls technician at a plastics injection molder", "maintenance supervisor at a sawmill", "facilities engineer at a cold-storage warehouse",
      "maintenance planner at a paper converting plant"],
     "Your plant has had unplanned motor and bearing failures this year and your budget is tight. You use X in the evening to keep up with people in your trade and the news.",
     ["predictive maintenance", "industrial automation", "fishing", "home renovation", "the Mariners", "personal finance", "woodworking", "local news"]),
 pop("generalists", 8,
     ["Juniper Ash", "Nikhil Bose", "Odette Marchand", "Felix Brandt", "Sunny Okoro", "Tobias Wren", "Mei Lin", "Paloma Cruz", "Grant Ellison", "Ines Duarte"],
     ["software engineer at a fintech startup", "product designer", "graduate student in mechanical engineering", "hobbyist maker who builds CNC machines",
      "data analyst at a hospital", "high-school physics teacher", "electrician"],
     "You use X on your phone a few times an evening to keep up with friends, tech and the city.",
     ["tech industry", "gadgets", "hiking", "cooking", "music", "climate", "3D printing", "startups"]),
 pop("buyers", 8,
     ["Gloria Fenwick", "Arturo Benitez", "Hollis Grant", "Priyanka Desai", "Walter Okonjo", "Beatrix Lund", "Simon Achterberg", "Fatima Zahir"],
     ["procurement manager at a mid-size food manufacturer", "plant manager at an auto-parts supplier", "operations director at a contract packager",
      "CFO of a family-owned machine shop", "independent maintenance consultant", "purchasing lead at a municipal water utility"],
     "You sign off on equipment and service spend and you get pitched by vendors every week. You use X in the evening for industry news and to see what peers recommend.",
     ["supply chain", "manufacturing", "golf", "personal finance", "sailing", "local politics", "leadership", "gardening"]),
]
ACCOUNTS = [
 {"site": "x-social", "handle": "ferrovane", "name": "Ferrovane", "location": "Tacoma, WA", "followers": 310,
  "bio": "Wireless vibration monitoring for small and mid-size plants. Pilot kits shipping now; follow for waitlist updates. (Fictional company in a ComputerWorld simulation.)"},
 {"site": "x-social", "handle": "mara_ferrovane", "name": "Mara Lindqvist", "location": "Tacoma, WA", "followers": 1240,
  "bio": "Co-founder and CEO @ferrovane. Ex-reliability engineer. (Fictional person in a ComputerWorld simulation.)"},
 {"site": "x-social", "handle": "plantfloor_wire", "name": "Plant Floor Weekly", "location": "Portland, OR", "followers": 8200,
  "bio": "Independent coverage of industrial technology, maintenance and operations. (Fictional outlet in a ComputerWorld simulation.)"},
 {"site": "x-social", "handle": "benchnotes_lab", "name": "Bench Notes Lab", "location": "Spokane, WA", "followers": 2900,
  "bio": "Independent test bench for industrial sensors. We buy what we test and publish our data. (Fictional lab in a ComputerWorld simulation.)"},
 {"site": "x-social", "handle": "teo_reliability", "name": "Teo Varga", "location": "Everett, WA", "followers": 640,
  "bio": "Reliability planner, 18 years on the plant floor. (Fictional person in a ComputerWorld simulation.)"},
 {"site": "x-social", "handle": "kat_runs_ops", "name": "Kat Moreno", "location": "Kent, WA", "followers": 520,
  "bio": "Plant operations. Coffee, uptime, forklifts. (Fictional person in a ComputerWorld simulation.)"},
]
PEOPLE = [p for cohort in PEOPLE for p in cohort]
COHORT = {pid: [f"people.{i}" for i in ids] for pid, ids in COHORT_IDS.items()}
for p in PEOPLE: p.pop("cohort")
def G(x):
    return {"PRAC": COHORT["practitioners"], "GEN": COHORT["generalists"], "BUY": COHORT["buyers"]}[x]
GRAPHS = [
 {"id": "friends", "service": "x-social", "over": "people", "generator": "watts_strogatz", "k": 4, "p": 0.2},
 {"id": "outlet-practitioners", "service": "x-social", "generator": "bernoulli", "p": 0.5, "from": G("PRAC"), "to": "accounts.plantfloor_wire"},
 {"id": "outlet-buyers", "service": "x-social", "generator": "bernoulli", "p": 0.5, "from": G("BUY"), "to": "accounts.plantfloor_wire"},
 {"id": "outlet-generalists", "service": "x-social", "generator": "bernoulli", "p": 0.15, "from": G("GEN"), "to": "accounts.plantfloor_wire"},
 {"id": "company", "service": "x-social", "generator": "bernoulli", "p": 0.1, "from": G("PRAC") + G("BUY"), "to": "accounts.ferrovane"},
 {"id": "founder", "service": "x-social", "generator": "bernoulli", "p": 0.15, "from": G("PRAC") + G("GEN") + G("BUY"), "to": "accounts.mara_ferrovane"},
 {"id": "lab", "service": "x-social", "generator": "bernoulli", "p": 0.3, "from": G("PRAC") + G("BUY"), "to": "accounts.benchnotes_lab"},
 {"id": "peers-teo", "service": "x-social", "generator": "bernoulli", "p": 0.3, "from": G("PRAC"), "to": "accounts.teo_reliability"},
 {"id": "peers-kat", "service": "x-social", "generator": "bernoulli", "p": 0.3, "from": G("PRAC") + G("BUY"), "to": "accounts.kat_runs_ops"},
]
AMBIENT = [
 ("practitioners-1", "a-1", "Third night shift in a row. At least the compressor behaved today."),
 ("practitioners-4", "a-2", "Anyone else's parts supplier quoting 14 weeks on bearings again?"),
 ("generalists-0", "a-3", "Finally got the CNC to cut clean aluminum. Only took six broken end mills."),
 ("generalists-3", "a-4", "Rain again. Seattle in November, who could have guessed."),
 ("buyers-2", "a-5", "Budget season. Every line item needs a story this year."),
 ("teo_reliability", "a-6", "Reminder that a bearing rarely fails without telling you first. Most of us just are not listening."),
 ("kat_runs_ops", "a-7", "Forklift certification day. Twelve people, one very patient instructor."),
 ("benchnotes_lab", "a-8", "Next on the bench: three low-cost current clamps. Data in two weeks."),
 ("plantfloor_wire", "a-9", "This week: why so many mid-size plants still run maintenance off a whiteboard."),
]
ART = {
 "announcement": ("ferrovane", "Ferrovane is launching the VX-4, a wireless vibration sensor for motors, pumps and fans. Each sensor samples three axes up to 6 kHz and reports to a local gateway; rated battery life is 24 months at one reading per minute. Pilot kits (6 sensors and a gateway) are $1,450; software is $39 per sensor per month. Shipping to US plants from December."),
 "article": ("plantfloor_wire", "Review: we ran Ferrovane's (@ferrovane) new VX-4 wireless vibration sensors on a test rig for two weeks. What holds up: three-axis sampling to 6 kHz, ten-minute installs per motor, and a local gateway that keeps raw data on site. Open questions: the 24-month battery figure is the vendor's, at one reading a minute, and trend analytics need a cloud account. Pilot kit $1,450, then $39 per sensor per month."),
 "founder": ("mara_ferrovane", "Today we launch the VX-4. After three years of building, small plants finally get the vibration monitoring big plants have had for decades: no wiring, ten-minute installs, 24-month batteries. Almost every bearing failure we have seen was predictable. Pilot kits are $1,450. If your line has ever stopped for a motor, follow @ferrovane and let's talk."),
 "discussion-1": ("teo_reliability", "We run wired accelerometers on our 40 critical motors and nothing on the other 200. At $39 a sensor a month the long tail starts to make sense. Curious how the gateway copes with a noisy VFD room."),
 "discussion-2": ("kat_runs_ops", "Ten-minute installs is the line that matters to us. Every vendor demo is great until you need an electrician and a shutdown window."),
 "critique": ("benchnotes_lab", "We bench-tested two VX-4 units against a reference accelerometer. Frequency response matched within 3 dB to 6 kHz, as claimed. Battery: at one reading a minute our units project 14 months, not 24 (method and data on benchnotes.example). Trend views need a cloud account; no on-prem option yet. Still the cheapest three-axis option per point we have tested."),
 "placebo": ("ferrovane", "Our team spent Saturday at the Puyallup river cleanup. Thanks to everyone who came out, and to the volunteers who brought coffee."),
}
def post(art, at=0, quoted=None):
    who, text = ART[art]
    p = {"id": art, "text": text}
    if quoted: p["quoted"] = quoted
    return {"op": "post", "as": who, "at": at, "service": "x-social", "post": p}
ARMS = [
 {"id": "control"},
 {"id": "announcement", "ops": [post("announcement")]},
 {"id": "article", "ops": [post("article")]},
 {"id": "founder", "ops": [post("founder")]},
 {"id": "article-discussion", "ops": [post("article"), post("discussion-1", 1, "article"), post("discussion-2", 2, "article")]},
 {"id": "article-critique", "ops": [post("article"), post("critique", 1, "article")]},
 {"id": "placebo", "ops": [post("placebo")]},
]
spec = {
 "schema_version": 1, "id": "launch-campaign", "world": "worlds/company-2026", "seed": 101,
 "clock": {"start": "2026-11-10T17:00:00-08:00", "quantum": "1h", "rounds": 8, "slot": "2m"},
 "structure": {"description": "Twenty-eight fictional readers in the Pacific Northwest (twelve plant maintenance practitioners, eight general tech readers, eight people who sign off on equipment spend) use X on their phones one evening while a fictional sensor company, Ferrovane, launches its VX-4. Each arm publishes a different artifact, or none. Every company, person, outlet and post here is fictional and exists only in this simulation."},
 "operation": {"observation": "semantic", "max_actions_per_activation": 6, "max_actions_per_session": 48},
 "people": PEOPLE, "cohorts": COHORT_IDS, "accounts": ACCOUNTS, "graphs": GRAPHS,
 "exogenous": [{"at": 0, "as": who, "do": {"service": "x-social", "post": {"id": pid, "text": text, "tick": 0}}} for who, pid, text in AMBIENT],
 "checks": [{"id": "members-act", "metric": "size(x-social.likes) + size(x-social.reposts) + count(x-social.posts where author in people)", "min": 1}],
 "outcomes": {
  "preregistered": [{"id": "ferrovane_followers", "metric": "sum(followers of x-social.accounts where handle = \"ferrovane\")", "direction": "increase", "at": "horizon"}],
  "secondary": [
   {"id": "founder_followers", "metric": "sum(followers of x-social.accounts where handle = \"mara_ferrovane\")"},
   {"id": "member_posts_ferrovane", "metric": "count(x-social.posts where text contains \"ferrovane\" and author in people)"},
   {"id": "all_reposts", "metric": "size(x-social.reposts)"},
   {"id": "all_likes", "metric": "size(x-social.likes)"}]},
 "interventions": {"design": "matched_fork", "fork_at": 0, "arms": ARMS},
 "sweep": {"replicates": [101, 102, 103, 104, 105]},
 "traces": {"actions": "all", "llm": "all", "scene_digests": "all", "divergence": "per_person", "deltas": ["x-social"]},
}
if __name__ == "__main__":
    out = pathlib.Path(__file__).resolve().parent / "spec/launch-campaign.json"
    out.write_text(json.dumps(spec, indent=1) + "\n")
    print(f"wrote {out}")
