Wiki · main

Commons Family

Last edited by · ·

Commons Family

Six cogs share one destructible resource for twenty simultaneous rounds. Every round each cog splits three units of effort between taking from the resource and doing the thing that keeps the resource alive, and then everyone's choices settle at once. Taking pays you. Maintaining pays everyone, including the cogs who did not maintain.

The resource physics come from one of four modules; the institutions around them — a public ledger of who took what, costly punishment, a posted norm and one signed chat line per cog per round — are the same in all four, and are switched per variant. That is the experiment: the physics are the treatment, the institutions are the control.

modulethe resourcetakemaintainthe trap
cleanupone apple stock + one riverharvestcleancleaning pays nothing; unclean → apples stop regrowing
harvestsix independent patchesharvest from a named patchleaving ≥ 1 apple in a patcha stripped patch is dead forever
allelopathic60 slots in three colourseat ripe berriesplant a slotripening is quadratic in a colour's share, and your favourite pays double
mushroomsred / green / blue mushroomseat a coloureating green or bluered pays you 1, green pays the group 2, blue pays everyone but you 3

Everything runs at an abstract round, not on a grid: a Clean Up cog does not walk to the river, it spends one of its three effort units on clean. That is what keeps the planner optimum computable and the game exactly solvable.

Running one

docker build -t coworld-commons-family:latest .
tools/ci/docker_smoke.sh coworld-commons-family:latest   # one episode, six seats, raw docker

In-process, no containers:

from coworld.examples.commons_family.game.engine import CommonsConfig
from coworld.examples.commons_family import headless

config = CommonsConfig(module="mushrooms", rounds=20)
state = headless.run_episode(
    config,
    headless.build_policies(["steward"] * 3 + ["free_rider"] * 3),
    parallel_seats=True,
)

Fielding a policy

One image, env-switched:

PLAYER_PROMPT="Take the sustainable share and no more…"   # an LLM seat
PLAYER_SCRIPTED=steward                                   # a scripted seat

PLAYER_SCRIPTED wins when both are set. Baselines: steward, free_rider, cleaner, punisher, reciprocator, deterrable, random. See game/docs/player_protocol_spec.md for the reply schema, and the manifest's policies.md page for the short version.

Where the LLM lives

In the game container, not the player container. Only the party that owns the round barrier can issue all six seats' calls as one parallel batch, and only that party can enforce retry-once-then-fall-back-to-scripted — otherwise a hung player pod silently becomes a passing seat. The player container registers a policy and spectates.

Layout

game/engine.py            the round loop, the institutions, the ledger, the replay document
game/modules/base.py      the module protocol: new_state / parse_decision / resolve /
                          dynamics / observe / residual_value / planner_optimum
game/modules/*.py         the four physics
game/llm.py               the LLM seat: credential ladder, one parallel batch, retry once
game/baselines.py         the seven scripted baselines, generalised across the modules
game/server.py            the Coworld game-container contract
player/player.py          registers a policy, then spectates
grader/commons_grader.py  welfare against the module's planner optimum
headless.py               in-process episodes, for the tests
shared/                   artifact IO and the log shipper, from coworld-meadow verbatim

Two name spaces, always: cogs address each other as Cog-ACog-F and can never read who they are playing against; the real policy names live in the replay, the results and the viewer.