Object-Oriented Prompt Engineering for JavaEst. 2026 · MIT License
PrOOPt

“A Java library that keeps LLMs on a need-to-know basis.”

Use Case · States No. 2

The Policy Wind Tunnel

Synthetic societies for decision rehearsal.

The Story

No aircraft flies before the wind tunnel. Yet every policy, price change, and public-health message still launches straight into the storm — tested on nobody, or tested on you.

Institutions that move markets and populations can model aggregates, and they can sample opinions. What they cannot do is rehearse the decision: subject a population of heterogeneous, memory-bearing individuals to an intervention, watch belief and behaviour evolve day by day — and then rewind the world and try the other version of the announcement.

The Policy Wind Tunnel does exactly that. It instantiates a census-calibrated synthetic population of @Persona citizens, subjects it to interventions via targeted broadcasts, lets second-order discourse spread through rumour physics, and reads the results as longitudinal, source-attributed state history. Every opinion shift is a database row with a cause.

How It Is Built

A population with a census-shaped spine

List<Citizen> city = citizens.generate()
        .limit(50_000)
        .seed("Adult residents of a mid-size rust-belt metro, 2026")
        .distribute(Segment.of("suburban homeowners, fixed-rate mortgages", 0.34)
                .pinTrait("housing_status", "OWNER_FIXED")
                .normalDistribution("rate_sensitivity", 35, 12)
                .normalDistribution("institutional_trust", 48, 15)
                .build())
        .distribute(Segment.of("urban renters, gig income", 0.22)
                .pinTrait("employment_mode", "GIG")
                .normalDistribution("rate_sensitivity", 72, 9)
                .build())
        .build();

Segment proportions, pinned traits, and normal distributions reproduce census margins by construction — not by hoping the model's priors land in the right place.

Citizens with inner lives

@Persona(
    value = "An adult resident of a mid-size metro with realistic financial "
          + "life, media diet, social ties, and bounded attention.",
    evolutionSchedule = "@daily",
    evolutionDescription = "Attention to last week's news decays; lived "
          + "financial pressure (rent, groceries, rate resets) reasserts "
          + "itself over headlines.",
    internalLoop = true, loopSchedule = "@daily",
    loopDepth = LoopDepth.MODERATE,
    loopDescription = "Reconcile what I heard today with what I believe and "
          + "what my neighbours are saying; settle on what I'd tell a pollster.")
public class Citizen {

    @Trait("Trust in public institutions, 0 (none) to 100 (full).")
    private int institutionalTrust;

    @Trait("Perceived personal financial pressure, 0–100.")
    private int financialPressure;

    @Trait("Current belief about the rate decision, in the citizen's own words.")
    private String narrativeBelief;

    @Trait(value = "Private running memory of recent events and conversations.",
           index = false)
    private String memory;
}

Interventions as targeted broadcasts; discourse as physics

The direct shock is delivered with a predicate — broadcast(rateAnnouncement, "media_diet = 'EVENING_NEWS'") — and then secondary transmission spreads through social ties via EventPipeline with BlastRadius.RELATIONSHIP and AttenuationModel.RUMOUR. Which means misinformation epidemiology — how a clear message curdles into a garbled one as it crosses a population — is an off-the-shelf experiment.

Readouts that are econometrics, not vibes

EvolutionReport decomposes each opinion shift by UpdateSource: direct effect (EXTERNAL), social contagion (EVENT), private reflection (INTERNAL_LOOP), and background drift (EVOLUTION). Cohort comparisons run through reportWhere(...) windows — treatment versus control — and aggregate claims always reconcile down to individual histories. Best of all, because trackHistory = true makes timelines forkable, you can restore the world to the day before the announcement and try the other wording. Survey panels cannot rewind. This can.

Why It Is Unprecedented

Agent-based models have heterogeneous agents but hand-coded, brittle rules. LLM-agent simulations have rich agents but opaque transcripts and irreproducible runs. The Wind Tunnel has both halves: rich, language-driven citizens whose every belief transition is a source-attributed row in prooopt_persona_history. It is computational social science with a chain of custody — and every experiment is a forkable timeline.

The Fine Print

Synthetic citizens are not citizens. Validity is bounded by the LLM's social priors and your calibration data; findings are hypotheses for real-world testing, not forecasts with confidence intervals. LLM priors carry cultural skew, so segment seeds and population rules must be calibrated per society and documented in the study record. And the dual-use concern is real: the same instrument that hardens a public-health message could tune a manipulative one. Deployments should carry use governance — and PrOOPt's audit-everything posture is the enforcement surface. Wind tunnels do not promise the flight; they catch the wing that falls off. Institutions buy wind tunnels.