Skip to content

Episode policy

Steven Enamakel edited this page Sep 5, 2026 · 4 revisions

Episode policy

Everything an episode is bounded and tuned by sits in one struct.

pub struct EpisodePolicy {
    pub turn_budget: u32,
    pub blind_round: bool,
    pub dominance_cap: u32,
    pub repetition_cap: u32,
    pub directory: Option<DirectoryPolicy>,
    pub defer_cap: Option<u32>,
    pub quorum: QuorumPolicy,
    pub weights: SalienceWeights,
}

The defaults

EpisodePolicy::DEFAULT = EpisodePolicy {
    turn_budget: 12,
    blind_round: true,
    dominance_cap: 50,
    repetition_cap: 3,
    directory: None,
    defer_cap: None,
    quorum: QuorumPolicy {
        threshold: 2,
        window: 30,
        require_grounded: true,
        refutation_cap: None,
        require_evidential: false,
    },
    weights: SalienceWeights { recency: 5, importance: 30, relevance: 20, half_life: 20 },
};

The budget is small for a reason with a name: conformity rises with interaction time, so a long episode buys correlated error rather than better judgement.

The salience weights follow the memory-stream retrieval score from Generative Agents. Where that implementation's paper and its code disagree, these follow the code, and decay is applied by rank in sequence rather than by elapsed time, which is what that implementation did and what a transcript with no clock can support.

Both narrowing knobs are off, and for the same reason: the benchmark scored them and they lost. refutation_cap: None records a !refute in TopicStanding::refuted_by and lets it cap nothing; require_evidential: false counts a support that cites another support the same as one that cites a measurement. Turning either on is a deliberate choice a host makes with the numbers in front of it, and the case for doing so is a room with a hidden profile — one member holding the fact that overturns an option everybody else likes — which is exactly what the simulated benchmark does not have.

Expert delegation is off too

DirectoryPolicy::DEFAULT = DirectoryPolicy {
    half_life: 20,
    specialisation: 30,
    credibility: 20,
    prior: 10,
    discredit: 20,
    window: 30,
    floor: 1_000,
};

directory: None folds no directory, so BidReason::Knows is unreachable and the attention market behaves exactly as it did before P10. defer_cap: None leaves a chain of !defer turns bounded only by turn_budget; Some(0) is rejected as an error rather than read as "off", because None is off.

A host that wants delegation writes the policy out:

let policy = EpisodePolicy {
    directory: Some(DirectoryPolicy::DEFAULT),
    defer_cap: Some(2),
    ..EpisodePolicy::DEFAULT
};

Both fields are required but nullable on the wire, the same shape refutation_cap uses. An absent key is a decode error, so a policy serialized before P10 fails loudly rather than quietly acquiring a default, and a host that means to leave the mechanism off has to say "directory": null.

It is off because the benchmark has not scored it yet, and the acceptance criteria were written before any numbers: it must be able to lose, vote gets the same budget, and a mechanism that helps hidden profiles but costs more than two points on the uniform bench ships off. On a room of uniform expertise the predicted gain is exactly zero — there is nothing to route on.

Tune the threshold with the desk

The quorum threshold is the single most consequential setting, and it has a bound on each side. Five members, 5000 rooms, everything else held constant:

quorum deadlocked exhausted decided % correct %
2 of 5, below a majority 514 0 89.7 73.3
3 of 5, smallest majority 0 29 99.4 82.1
5 of 5, unanimity 0 2092 58.2 55.2

Below a majority, rooms deadlock. Five members can put two grounded supporters behind each of two options, and an episode where two options both carry is deadlocked by definition. No amount of further support resolves it, because both stay above the line. Requiring a majority makes that state unreachable and the deadlock rate falls to zero.

At unanimity, rooms cannot finish. Cross-inhibition removes a silenced advocate from a topic's supporter set and does not put them back, so a single grounded !object makes quorum unreachable for the rest of the episode. Two in five episodes then spend the whole budget without deciding.

So: a majority of the desk, and never the whole of it.

let threshold = (agents / 2 + 1).min(agents - 1);

A three-member desk is where this bites hardest, because a threshold of three there is unanimity. That case was found in a live room before it was measured.

Scale the budget with the desk too

A fixed budget makes a larger room look worse than a smaller one, and the effect is entirely an artifact of the cap. An eight-member room, 1500 rooms each:

budget decided % correct % turns actually spent
12 65.3 63.1 10.36
16 89.6 82.9 10.96
20 94.5 86.3 11.24
24 96.4 87.7 11.42

A blind opening round costs one turn per member before anybody has seen anybody, a majority then has to assemble on one option, and the decision has to be recorded. Three turns per member covers that, and it is a cap rather than a cost: the eight-member room finishes in 11.4 of the 24 turns it is allowed. At five members, budgets of 15, 20 and 25 score 82.0, 82.1 and 82.1, so past the point where the room can finish, extra budget buys nothing.

Keep the blind round on

Five members, 5000 rooms:

opening round decided % correct %
blind 99.4 82.1
full visibility 100.0 58.0

With full visibility from the first turn the room cascades onto whatever was proposed first and lands level with a single agent. That is an information cascade, and Visibility::Blind is what prevents it. Condorcet's jury theorem is the formal reason it matters: pooling beats an individual only while the individuals are independent.

Sweeping it yourself

cargo run --release -p tinyhivemind-hive --example bench -- --sweep

The grid is swept relative to desk size rather than in absolute numbers, since both consequential settings scale with the room. Benchmarks has the rest, Hive episodes has what each setting acts on, and Further reading has where the mechanisms come from. The implementation is src/episode/types.rs.

Clone this wiki locally