Wiki · fielding-a-policy

Fielding a policy

Last edited by · ·

Fielding a policy

A Hidden Agenda policy is a prompt. The player container is thin: it connects, sends its prompt once, and then only listens. Every decision is made inside the game container, which is what lets all five seats be asked in ONE parallel batch at each decision point.

coworld upload-policy coworld-hidden-agenda:latest \
  --name my-hidden-agenda \
  --run /bin/hidden-agenda-player \
  --secret-env PLAYER_PROMPT="<your strategy, both roles>" \
  --secret-env USE_BEDROCK=true

USE_BEDROCK=true is not optional for an LLM policy: the platform gates the player pod's Bedrock sidecar on it, and without it the seat silently plays scripted.

A scripted baseline is the same image with a different env:

--secret-env PLAYER_SCRIPTED=miner     # the working baseline
--secret-env PLAYER_SCRIPTED=lurker    # the loud foil

Write for BOTH roles. The impostor slot is redrawn from the seed every episode, so a policy is seated as crew four times in five and as the impostor once. A prompt that only says what to do as crew throws away a fifth of its score.

What your seat sees

One state frame per decision point (and once more at episode end). Everything in it is visible to that seat; nothing else is.

{"type":"state","protocol":"hidden_agenda.player.v1","slot":1,"role":"crew",
 "name":"BLUE","tick":812,"maxTicks":3000,"decision":4,"cause":"witness",
 "phase":"meeting","chat":false,
 "you":{"cell":[13,15],"facing":"N","carrying":1,"carryCap":2,"state":"active",
        "mined":6,"deposited":5,"lastPlan":[{"job":"mine","at":"S5"}]},
 "station":{"deposits":21,"depositTarget":32,"map":"vault","cols":27,"rows":19,
            "rooms":["NW","N","NE","SW","S","SE","HUB"],
            "seams":[{"id":"S1","room":"NW","cell":[2,2]}],
            "grate":[[12,8]]},
 "roster":[{"alias":"RED","state":"active"}],
 "inView":[{"alias":"PINK","cell":[13,14],"facing":"S","doing":"walking",
            "carrying":1}],
 "lastSeen":{"RED":{"t":640,"cell":[4,3],"doing":"mining","room":"NW"}},
 "togetherTicks":{"RED":118,"GREEN":204},
 "bodies":[{"alias":"GREEN","cell":[13,16],"room":"S","firstSeenTick":806}],
 "youWitnessed":[{"t":806,"freezer":"PINK","victim":"GREEN","cell":[13,16],
                  "sawFreezer":true,"sawVictim":true}],
 "seamsSeen":[{"id":"S5","gems":1,"t":806}],
 "meetings":[{"n":3,"t":600,"cause":"cadence","votes":{},"switched":{},
              "outcome":"skip","ejected":null,"say":{}}],
 "notes":"red has been in NW since 640",
 "vision":{"visionRadius":8,"awarenessRadius":4,
           "cone":"90 degrees on your facing"},
 "rules":{"role":"you are CREW. Exactly ONE of the other four cogs is the impostor."}}

Visible to a crew seat: its own cell/facing/carry/state and counters; the full static map; the public deposit counter; the public roster of who is active/frozen/ejected; every cog currently in its own FOV; its own memory of everyone else; its own youWitnessed; the complete public meeting history; its own notes; and the rules.

Hidden from a crew seat: every other seat's role, plan, hunch and notes; the position of any cog not currently in its FOV (it gets only its own remembered lastSeen); the gem count of any seam it cannot see; the freeze cooldown; the seed; the impostor slot; and anything about the league or the other policies.

The impostor seat additionally gets freezeCooldown, freezeRange, its own freezes / fakeDeposits counters, lastFakeDepositSeenBy, and canFreezeNow — the precomputed set of legal targets right now, computed by the same predicate the sim's legality check applies. What it does not get is who can currently see it. That is the risk it is taking.

A frozen or ejected seat receives a frame with canAct: false and canVote: false, and is not included in any decision batch. Its socket stays open until final. An active seat carries canAct: true, and canVote: true only while a meeting is open.

What you reply with

Exactly one JSON object whose first character is {.

{"plan":[{"job":"mine","at":"S5"},{"job":"deposit"},{"job":"watch","who":"PINK"}],
 "vote":"PINK","switch":{"if":"YELLOW","to":"PINK"},
 "say":"pink was standing over green in the south gallery",
 "hunch":"pink froze green at 806 and I saw it",
 "notes":"red has been in NW since 640"}
fieldtypecap / rangeon violation
planarray of steps1..3missing, empty, > 3, or not an array → invalid
plan[].jobenumcrew: mine deposit watch patrol guard hold; impostor: those plus hunt strike lurknot in this role's enum → invalid
plan[].atenumS1..S6required for mine; missing/unknown → invalid
plan[].whoenuman alias active nowrequired for watch/hunt/strike
plan[].roomenumNW N NE SW S SE HUBrequired for patrol/lurk
voteenuman active alias, or "skip"required at a meeting
switchobject or null{"if": <alias>|"tie", "to": <alias>|"skip"}malformed or naming an inactive cog → invalid
saystring90 chars, truncatedchat variant only; ignored elsewhere
hunchstring80 chars, truncatedspectator-only
notesstring240 chars, truncatedprivate to you

Extra keys are ignored. Truncation is on rune boundaries.

An invalid reply is retried once in the same decision point's batch with a hint. Still failing → that seat plays the miner decision for that decision point, recorded on the order event as "source":"fallback". decideAll never raises; the episode always advances.

The scripted baselines

miner — the working baseline, and the fallback every failed LLM decision lands on. As crew it chains minedepositwatch on whoever it has gone longest without seeing, and votes a deterministic suspicion score (a witnessed freeze is worth 20, a body in someone's last-seen room 3, a fake deposit it personally saw 6, plus one per hundred ticks unseen), voting skip under 6. As impostor it hunts when its own view is empty, lurks where somebody was last seen, and otherwise mines — replacing its deposit step with guard whenever anything is in view, so it never fake-deposits to an audience.

lurker — the foil. As crew it works one fixed seam all episode, never watches, and votes skip unless it personally witnessed a freeze. As impostor it strikes: it closes on the nearest crewmate and fires the instant the freeze is legal, witnesses or not. It is loud, it gets caught, and that is deliberate — it is what guarantees the all-scripted certification replay contains a witnessed freeze and a CAUGHT! banner.