Governance Meta-Agents¶
A governance meta-agent is a non-playing observer that watches an
arena (C3.a) or an entire World (C3.b) and emits perturbation specs
in response to what it sees. Meta-agents do not harvest, do not have
a decide() loop, and are invisible to the per-arena scheduler. Each
turn (C3.a) or each global round (C3.b), the arena or World calls
and the agent returns an array of perturbation specs. The specs are
routed through the standard applyPerturbation() entry, so existing
perturbation types (sanction, wealth_shock, exclude, …) are
available without extra plumbing.
This page documents the registry, the two scopes (C3.a vs C3.b), and the four concrete strategies shipped in v1.0.
The registry¶
| Key | Class | Scope |
|---|---|---|
noop |
GovernanceAgent (base) |
C3.a / C3.b probe |
sanctioner |
SanctionerAgent |
C3.a — arena-level |
voluntary_sanctioner |
VoluntarySanctionerAgent |
C3.a — arena-level |
world_sanctioner |
WorldSanctionerAgent |
C3.b — world-level |
treaty_enforcer |
TreatyEnforcerAgent |
C3.b — world-level |
pollution_regulator |
PollutionRegulatorAgent |
S3 — world-level (Seneca) |
Adding a new strategy is a 30–80 line operation:
- Extend
GovernanceAgent, overrideobserve(). - Register in
GOVERNANCE_REGISTRY. - Add a doc entry here.
No changes to Arena, World, or the perturbation engine are
required. This is the same engine-agnostic pattern as the engine
plugin registry — the substrate stays out of the strategy layer.
Two scopes: C3.a vs C3.b¶
| Aspect | C3.a (arena-level) | C3.b (world-level) |
|---|---|---|
| Attached to | One Arena |
One World |
observe() arguments |
(state, turnRecord, arena) |
(snapshot, roundResult, world) |
| Invoked | Once per arena turn, after the harvest loop | Once per global round, after link flows settle |
| Perturbation routing | Arena-local (no target) |
target: { arenaIds: [aid] } per spec |
| Wired through | Arena.addAgent({ role: 'meta', strategy }) |
World.addMetaAgent({ strategy }) |
| HTTP variant in v1.0 | No (builtin only) | No (builtin only) |
The split is orthogonal to the agent type: a C3.a Sanctioner and a
C3.b WorldSanctioner can coexist on the same federation — one
watches a single arena's pact violations, the other rolls across all
of them.
C3.a — sanctioner¶
Reflexive arena-level enforcer. Applies a fixed-amount monetary
sanction to every pact violator detected this turn. Mirrors the
behaviour of the legacy pact_violation-triggered sanction
perturbation from v0.9 C1, but routed through the agent channel so
that richer policies (next entry) can swap in without touching the
schedule layer.
arena.addAgent({
id: 'meta-1',
role: 'meta',
agentType: 'builtin',
strategy: 'sanctioner',
strategyParams: { amount: 3 },
});
| Param | Default | Meaning |
|---|---|---|
amount |
3 |
Flat sanction per violation |
Each emitted spec:
C3.a — voluntary_sanctioner¶
Discretion-based arena-level enforcer. Unlike sanctioner (fires on
every violation), this agent decides whether to sanction based on
global commons state and per-party history. Closes the design-doc
"Yoon-style voluntary sanctioning" feature note.
Two complementary gates — both must hold for a violation to be sanctioned:
| Param | Default | Meaning |
|---|---|---|
amount |
3 |
Flat sanction |
triggerStockRatio |
0.5 |
Only fire when stock / capacity < ratio. Agent "waits and watches" while commons is healthy |
repeatThreshold |
0 |
Minimum number of prior violations by the offending party before sanctioning kicks in |
The prior-violation counter is per-party, kept in _priorViols for
the lifetime of the agent instance. Counted at observe() time and
incremented after the gate decision, so the first violation
triggers iff repeatThreshold == 0.
Mapping to Yoon's "voluntary" axis. The meta-agent doesn't pay a monetary cost (it has no wealth), but it does exercise discretion, which is the operational signal the design doc asked for. Cost-bearing voluntary sanctioning by a peer player belongs to a future role-mixing variant.
Reason string format:
C3.b — world_sanctioner¶
World-level mirror of sanctioner. A single instance covers an
entire federation: each global round it scans every arena that ticked
this round, finds the pact violations in the most recent turn, and
emits one sanction spec per violation, targeted via
{ arenaIds: [aid] }.
world.addMetaAgent({
strategy: 'world_sanctioner',
strategyParams: { amount: 3 },
displayName: 'enforcer',
});
| Param | Default | Meaning |
|---|---|---|
amount |
3 |
Flat sanction per violation |
The "ticked this round" filter (based on roundResult.ticks) keeps
behaviour correct when an arena auto-closes during its current round:
the final violations are still in its history and deserve a sanction.
Reason string format: world_sanction(<termType>).
C3.b — treaty_enforcer¶
World-level enforcer that monitors one named Treaty across its signatory arenas. The v1.0 enforcer recognises a single payload type:
Each global round, for every signatory arena that ticked:
- Sum
decisions[].actualacross the players in the last turn record →totalHarvest. - If
totalHarvest > perRoundPerArena, emit awealth_shocktargeting all members of the offending arena.
world.addMetaAgent({
strategy: 'treaty_enforcer',
strategyParams: {
treatyName: 'cap_8',
sanctionFactor: 0.9,
sanctionDelta: null,
},
});
| Param | Default | Meaning |
|---|---|---|
treatyName |
required | Treaty.name to monitor |
sanctionFactor |
0.9 |
Wealth multiplier on every player of the offending arena |
sanctionDelta |
null |
Optional flat wealth delta applied alongside the factor |
Collective sanction semantics: the wealth shock hits the entire community of the offending arena, not the individual high harvesters. The signatory community failed to police itself; the enforcement is at the membership level.
Reason string format:
Strategies in combination¶
C3.a and C3.b are independent. A typical v1.0 scenario combines:
| Layer | Role |
|---|---|
C3.a voluntary_sanctioner |
Catch single-arena pact violations with discretion |
C3.b treaty_enforcer |
Catch arena-collective breaches of a cross-arena cap |
The two layers act on different decision shapes and never overlap.
The treaty enforcer's wealth_shock and the C3.a sanctioner's
sanction are both routed through the same perturbation engine, so
the audit log is unified — every meta-action carries a reason
string identifying its source.
What governance meta-agents are not¶
- They are not players. No harvest, no wealth, no
decide(), invisible to the scheduler. - They are not engines. They never branch on
engineMode; the same governance code runs over logistic and Seneca commons unchanged. - They are not pacts. Pacts are signed by players and enforced by meta-agents; the two layers are orthogonal.
- They are not the only way to enforce. Direct
schedule-injected
sanctionperturbations remain available for scripted scenarios; the agent channel is the policy channel.
See also¶
- World — the layer that hosts C3.b meta-agents.
- Treaties — the cross-arena constraints that
treaty_enforcerreads. - Perturbations —
sanction,wealth_shock,excludespecs emitted by meta-agents. - Agents — overview of the four agent types and the
role: 'meta'flag.