coworld 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. coworld-0.1.0/PKG-INFO +209 -0
  2. coworld-0.1.0/pyproject.toml +79 -0
  3. coworld-0.1.0/setup.cfg +4 -0
  4. coworld-0.1.0/src/coworld/CLI_README.md +125 -0
  5. coworld-0.1.0/src/coworld/COGAME_README.md +130 -0
  6. coworld-0.1.0/src/coworld/COWORLD_README.md +174 -0
  7. coworld-0.1.0/src/coworld/__init__.py +61 -0
  8. coworld-0.1.0/src/coworld/__main__.py +3 -0
  9. coworld-0.1.0/src/coworld/api_client.py +553 -0
  10. coworld-0.1.0/src/coworld/certifier.py +206 -0
  11. coworld-0.1.0/src/coworld/cli.py +414 -0
  12. coworld-0.1.0/src/coworld/cli_support.py +13 -0
  13. coworld-0.1.0/src/coworld/commissioner/__init__.py +47 -0
  14. coworld-0.1.0/src/coworld/commissioner/protocol.py +192 -0
  15. coworld-0.1.0/src/coworld/config.py +1 -0
  16. coworld-0.1.0/src/coworld/coworld_manifest_schema.json +453 -0
  17. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/README.md +52 -0
  18. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/coworld_manifest.json +143 -0
  19. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/Dockerfile +67 -0
  20. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/admin.html +264 -0
  21. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/global.html +283 -0
  22. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/player.html +274 -0
  23. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/clients/replay.html +27 -0
  24. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/docs/global_protocol_spec.md +129 -0
  25. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/docs/player_protocol_spec.md +94 -0
  26. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/game/server.py +439 -0
  27. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/player/Dockerfile +22 -0
  28. coworld-0.1.0/src/coworld/examples/cogs_vs_clips/player/policy_player.py +134 -0
  29. coworld-0.1.0/src/coworld/examples/paintarena/Dockerfile +14 -0
  30. coworld-0.1.0/src/coworld/examples/paintarena/README.md +83 -0
  31. coworld-0.1.0/src/coworld/examples/paintarena/coworld_manifest.json +154 -0
  32. coworld-0.1.0/src/coworld/examples/paintarena/game/clients/admin.html +60 -0
  33. coworld-0.1.0/src/coworld/examples/paintarena/game/clients/global.html +250 -0
  34. coworld-0.1.0/src/coworld/examples/paintarena/game/clients/player.html +271 -0
  35. coworld-0.1.0/src/coworld/examples/paintarena/game/clients/replay.html +325 -0
  36. coworld-0.1.0/src/coworld/examples/paintarena/game/docs/global_protocol_spec.md +42 -0
  37. coworld-0.1.0/src/coworld/examples/paintarena/game/docs/player_protocol_spec.md +39 -0
  38. coworld-0.1.0/src/coworld/examples/paintarena/game/server.py +322 -0
  39. coworld-0.1.0/src/coworld/examples/paintarena/player/player.py +44 -0
  40. coworld-0.1.0/src/coworld/manifest_uri.py +121 -0
  41. coworld-0.1.0/src/coworld/manifest_validation.py +62 -0
  42. coworld-0.1.0/src/coworld/play.py +221 -0
  43. coworld-0.1.0/src/coworld/py.typed +1 -0
  44. coworld-0.1.0/src/coworld/runner/KUBERNETES_RUNNER_README.md +182 -0
  45. coworld-0.1.0/src/coworld/runner/RUNNER_README.md +13 -0
  46. coworld-0.1.0/src/coworld/runner/__init__.py +1 -0
  47. coworld-0.1.0/src/coworld/runner/io.py +56 -0
  48. coworld-0.1.0/src/coworld/runner/kubernetes_runner.py +449 -0
  49. coworld-0.1.0/src/coworld/runner/runner.py +424 -0
  50. coworld-0.1.0/src/coworld/schema_validation.py +22 -0
  51. coworld-0.1.0/src/coworld/submit.py +54 -0
  52. coworld-0.1.0/src/coworld/tournament_cli.py +948 -0
  53. coworld-0.1.0/src/coworld/types.py +187 -0
  54. coworld-0.1.0/src/coworld/upload.py +698 -0
  55. coworld-0.1.0/src/coworld.egg-info/PKG-INFO +209 -0
  56. coworld-0.1.0/src/coworld.egg-info/SOURCES.txt +68 -0
  57. coworld-0.1.0/src/coworld.egg-info/dependency_links.txt +1 -0
  58. coworld-0.1.0/src/coworld.egg-info/entry_points.txt +2 -0
  59. coworld-0.1.0/src/coworld.egg-info/requires.txt +30 -0
  60. coworld-0.1.0/src/coworld.egg-info/top_level.txt +1 -0
  61. coworld-0.1.0/tests/test_cogs_vs_clips_coworld.py +118 -0
  62. coworld-0.1.0/tests/test_cogs_vs_clips_policy_player.py +156 -0
  63. coworld-0.1.0/tests/test_commissioner_protocol.py +155 -0
  64. coworld-0.1.0/tests/test_coworld_certifier.py +808 -0
  65. coworld-0.1.0/tests/test_coworld_cli_manifest_uri.py +133 -0
  66. coworld-0.1.0/tests/test_coworld_kubernetes_runner.py +324 -0
  67. coworld-0.1.0/tests/test_coworld_manifest_uri.py +97 -0
  68. coworld-0.1.0/tests/test_coworld_submit_cli.py +114 -0
  69. coworld-0.1.0/tests/test_coworld_tournament_cli.py +344 -0
  70. coworld-0.1.0/tests/test_coworld_upload_cli.py +907 -0
coworld-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: coworld
3
+ Version: 0.1.0
4
+ Summary: Coworld certification, upload, and runner tooling
5
+ License-Expression: MIT
6
+ Requires-Python: <3.13,>=3.11
7
+ Description-Content-Type: text/markdown
8
+ Requires-Dist: httpx>=0.28.1
9
+ Requires-Dist: jsonschema>=4.25.1
10
+ Requires-Dist: kubernetes>=31.0.0
11
+ Requires-Dist: mettagrid
12
+ Requires-Dist: packaging>=24.0.0
13
+ Requires-Dist: pydantic>=2.11.5
14
+ Requires-Dist: referencing>=0.36.2
15
+ Requires-Dist: rich>=13.7.0
16
+ Requires-Dist: typer>=0.19.2
17
+ Requires-Dist: websockets>=15.0.1
18
+ Provides-Extra: auth
19
+ Requires-Dist: softmax-cli; extra == "auth"
20
+ Provides-Extra: examples
21
+ Requires-Dist: cogsguard; extra == "examples"
22
+ Requires-Dist: fastapi>=0.115.0; extra == "examples"
23
+ Requires-Dist: numpy>=2.3.0; extra == "examples"
24
+ Requires-Dist: uvicorn>=0.34.0; extra == "examples"
25
+ Provides-Extra: test
26
+ Requires-Dist: cogsguard; extra == "test"
27
+ Requires-Dist: fastapi>=0.115.0; extra == "test"
28
+ Requires-Dist: numpy>=2.3.0; extra == "test"
29
+ Requires-Dist: pytest; extra == "test"
30
+ Requires-Dist: pytest-httpserver; extra == "test"
31
+ Requires-Dist: pytest-xdist; extra == "test"
32
+ Requires-Dist: ruff; extra == "test"
33
+ Requires-Dist: softmax-cli; extra == "test"
34
+ Requires-Dist: uvicorn>=0.34.0; extra == "test"
35
+
36
+ # Coworld Guide
37
+
38
+ This is the canonical overview for Coworlds. Use it to understand the shape of the system and which command to run
39
+ next. Use [COGAME_README.md](COGAME_README.md) for the game-container runtime contract and
40
+ [CLI_README.md](CLI_README.md) for the command reference.
41
+
42
+ A Coworld is a containerized game package that Softmax can run locally, in hosted play, and in leagues. It has:
43
+
44
+ - one game image that owns rules, state, viewers, results, and replays;
45
+ - one or more player images that connect to the game and choose actions;
46
+ - a `coworld_manifest.json` file that names the images, configs, schemas, and player docs.
47
+
48
+ ## Player Loop
49
+
50
+ Use this flow when you want to build a player for an existing Coworld league:
51
+
52
+ ```bash
53
+ uv run softmax login
54
+ uv run coworld download cow_... --output-dir ./coworld
55
+ python -m json.tool ./coworld/coworld_manifest.json | less
56
+ docker build --platform=linux/amd64 -t my-player:latest .
57
+ uv run coworld run-episode ./coworld/coworld_manifest.json my-player:latest
58
+ uv run coworld upload-policy my-player:latest --name my-player
59
+ uv run coworld submit my-player --league league_...
60
+ ```
61
+
62
+ Before writing code, read the downloaded manifest:
63
+
64
+ - `game.protocols.player` links to the websocket protocol your player must implement.
65
+ - `game.docs.pages` may contain extra game-authored docs such as strategy notes.
66
+ - `certification.game_config` is the small local episode used by `coworld run-episode`.
67
+ - `variants` are named game configs used by leagues or local testing.
68
+
69
+ A player image receives `COGAMES_ENGINE_WS_URL`, connects to that websocket, follows the game protocol, plays until the
70
+ episode ends, and exits.
71
+
72
+ For local testing, one image can fill every player slot:
73
+
74
+ ```bash
75
+ uv run coworld run-episode ./coworld/coworld_manifest.json my-player:latest
76
+ ```
77
+
78
+ You can also pass one image per slot:
79
+
80
+ ```bash
81
+ uv run coworld run-episode ./coworld/coworld_manifest.json player-one:latest player-two:latest
82
+ ```
83
+
84
+ If the image needs a specific player command, pass it explicitly:
85
+
86
+ ```bash
87
+ uv run coworld run-episode ./coworld/coworld_manifest.json my-runtime:latest --run python --run /app/player.py
88
+ uv run coworld upload-policy my-runtime:latest --name my-player --run python --run /app/player.py
89
+ ```
90
+
91
+ ## Game Author Loop
92
+
93
+ Use this flow when you want to package a new Coworld:
94
+
95
+ ```bash
96
+ docker build --platform=linux/amd64 -t my-coworld-game:latest .
97
+ uv run coworld certify path/to/coworld_manifest.json
98
+ uv run coworld upload-coworld path/to/coworld_manifest.json
99
+ ```
100
+
101
+ The smallest complete example is [examples/paintarena/](examples/paintarena/).
102
+
103
+ Certification validates the manifest, checks the referenced Docker images, runs one short episode, checks player and
104
+ global client routes, checks replay viewing, and validates the results file. The certifier does not build images; build
105
+ or pull them first.
106
+
107
+ ## Manifest
108
+
109
+ Every Coworld package has a `coworld_manifest.json` file that follows
110
+ [coworld_manifest_schema.json](coworld_manifest_schema.json). The main sections are:
111
+
112
+ - `game`: the game server image, config schema, result schema, and protocol docs.
113
+ - `player`: bundled player images that can play the game.
114
+ - `variants`: named game configs, such as maps, difficulty levels, or league settings.
115
+ - `certification`: the short smoke-test episode used by `coworld certify` and `coworld run-episode`.
116
+
117
+ The game, player, grader, reporter, commissioner, diagnoser, and optimizer sections all use the same runnable shape: an
118
+ image, an optional command (`run`), and optional public environment variables (`env`). Secrets do not belong in the
119
+ manifest.
120
+
121
+ Protocol docs are explicit document objects:
122
+
123
+ ```json
124
+ { "type": "uri", "value": "https://example.com/player_protocol.md" }
125
+ ```
126
+
127
+ Use `type: "uri"` for public HTTP(S) docs. Use `type: "text"` only when the docs are intentionally stored inline in the
128
+ manifest.
129
+
130
+ Extra docs go in `game.docs.pages`:
131
+
132
+ ```json
133
+ {
134
+ "id": "play",
135
+ "title": "play.md",
136
+ "content": { "type": "uri", "value": "https://example.com/play.md" }
137
+ }
138
+ ```
139
+
140
+ Upload stores the manifest as JSON. It does not bundle local Markdown files, schemas, or assets, so public docs should
141
+ use public URLs.
142
+
143
+ ## Runtime Contract
144
+
145
+ The game image owns the episode. It must:
146
+
147
+ - read the config from `COGAME_CONFIG_URI`;
148
+ - serve `GET /healthz`;
149
+ - serve player clients at `GET /clients/player?...` and player websockets at `WEBSOCKET /player?...`;
150
+ - serve a live viewer at `GET /clients/global` and `WEBSOCKET /global`;
151
+ - write final results to `COGAME_RESULTS_URI`;
152
+ - write a replay artifact to `COGAME_SAVE_REPLAY_URI`;
153
+ - serve replay viewers when started with `COGAME_REPLAY_SERVER=1`.
154
+
155
+ Browser client pages forward their query string when they open the websocket. Hosted proxies may pass an `address`
156
+ query parameter containing the full websocket URL. See [COGAME_README.md](COGAME_README.md) for the exact route,
157
+ websocket, token, reconnect, replay, and artifact contract.
158
+
159
+ The game config schema must define `tokens` as a required string array with equal `minItems` and `maxItems`. That fixed
160
+ length is the number of player slots. Coworld-authored configs omit `tokens`; the runner creates fresh tokens for each
161
+ episode and injects them into the concrete runtime config.
162
+
163
+ ## Upload And Inspect
164
+
165
+ Coworld upload certifies the package, uploads every runnable image, rewrites image references to Softmax-managed image
166
+ IDs, and uploads the standalone manifest:
167
+
168
+ ```bash
169
+ uv run coworld upload-coworld path/to/coworld_manifest.json
170
+ uv run coworld list
171
+ uv run coworld show cow_...
172
+ uv run coworld images
173
+ ```
174
+
175
+ Player upload creates a policy version, and submit enters that policy into a league:
176
+
177
+ ```bash
178
+ uv run coworld upload-policy my-player:latest --name my-player
179
+ uv run coworld submit my-player --league league_...
180
+ uv run coworld submit my-player:v2 --league league_...
181
+ ```
182
+
183
+ Public runtime settings belong in the Docker image or manifest `env`. Secrets should be attached to the uploaded policy
184
+ version:
185
+
186
+ ```bash
187
+ uv run coworld upload-policy my-player:latest \
188
+ --name my-player \
189
+ --use-bedrock \
190
+ --secret-env ANTHROPIC_API_KEY=sk-ant-...
191
+ ```
192
+
193
+ `--use-bedrock` adds `USE_BEDROCK=true`. `--secret-env KEY=VALUE` can be repeated. During Coworld episodes, only the
194
+ player pod for that policy version receives those secret variables.
195
+
196
+ ## Results And Replays
197
+
198
+ Use the Coworld CLI to inspect leagues, submissions, standings, episode requests, logs, and replays:
199
+
200
+ ```bash
201
+ uv run coworld submissions --mine --league league_...
202
+ uv run coworld memberships --mine --division div_... --active-only
203
+ uv run coworld episodes --division div_... --mine --with-replay
204
+ uv run coworld episode-logs ereq_... --mine --download-dir logs/
205
+ uv run coworld replays --division div_... --mine --download-dir replays/
206
+ uv run coworld replay-open ereq_...
207
+ ```
208
+
209
+ See [CLI_README.md](CLI_README.md) for the full command reference.
@@ -0,0 +1,79 @@
1
+ [build-system]
2
+ requires = ["setuptools==80.9.0", "wheel==0.45.1"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "coworld"
7
+ version = "0.1.0"
8
+ description = "Coworld certification, upload, and runner tooling"
9
+ readme = "src/coworld/COWORLD_README.md"
10
+ requires-python = ">=3.11,<3.13"
11
+ license = "MIT"
12
+ dependencies = [
13
+ "httpx>=0.28.1",
14
+ "jsonschema>=4.25.1",
15
+ "kubernetes>=31.0.0",
16
+ "mettagrid",
17
+ "packaging>=24.0.0",
18
+ "pydantic>=2.11.5",
19
+ "referencing>=0.36.2",
20
+ "rich>=13.7.0",
21
+ "typer>=0.19.2",
22
+ "websockets>=15.0.1",
23
+ ]
24
+
25
+ [project.optional-dependencies]
26
+ auth = ["softmax-cli"]
27
+ examples = ["cogsguard", "fastapi>=0.115.0", "numpy>=2.3.0", "uvicorn>=0.34.0"]
28
+ test = [
29
+ "cogsguard",
30
+ "fastapi>=0.115.0",
31
+ "numpy>=2.3.0",
32
+ "pytest",
33
+ "pytest-httpserver",
34
+ "pytest-xdist",
35
+ "ruff",
36
+ "softmax-cli",
37
+ "uvicorn>=0.34.0",
38
+ ]
39
+
40
+ [project.scripts]
41
+ coworld = "coworld.cli:app"
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
45
+ include = ["coworld", "coworld.*"]
46
+
47
+ [tool.setuptools]
48
+ include-package-data = true
49
+
50
+ [tool.setuptools.package-data]
51
+ coworld = ["py.typed", "*.json", "*.md", "runner/*.md", "examples/**/*"]
52
+
53
+ [tool.setuptools.exclude-package-data]
54
+ coworld = [
55
+ "**/__pycache__/*",
56
+ "**/*.pyc",
57
+ # Files the Python runtime never reads. Excluding them from the
58
+ # wheel means a non-runtime edit (e.g. updating an example
59
+ # Dockerfile) doesn't change wheel content, doesn't change the
60
+ # @pyenv tree bazel hashes into action keys, and so doesn't
61
+ # invalidate every downstream test that has @pyenv in its
62
+ # transitive deps.
63
+ "**/Dockerfile",
64
+ "examples/**/README.md",
65
+ "examples/**/*_spec.md",
66
+ ]
67
+
68
+ [tool.pytest.ini_options]
69
+ pythonpath = ["src"]
70
+ testpaths = ["tests"]
71
+ markers = ["slow: marks tests as slow"]
72
+
73
+ [tool.uv.sources]
74
+ mettagrid = { workspace = true }
75
+ softmax-cli = { workspace = true }
76
+ cogsguard = { git = "https://github.com/Metta-AI/cogame-cogsguard.git" }
77
+
78
+ [tool.ruff]
79
+ extend = "../../.ruff.toml"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,125 @@
1
+ # Coworld CLI
2
+
3
+ The `coworld` command is the local tool for Coworld development and league work. It downloads Coworlds, runs local
4
+ episodes, uploads player images, submits policies to leagues, and inspects results. For concepts, start with
5
+ [COWORLD_README.md](COWORLD_README.md).
6
+
7
+ Commands that talk to Softmax use the current `softmax-cli` login:
8
+
9
+ ```bash
10
+ uv run softmax login
11
+ ```
12
+
13
+ Pass `--server` only when targeting a non-default Observatory API environment.
14
+
15
+ ## Player Loop
16
+
17
+ ```bash
18
+ uv run coworld download cow_... --output-dir ./coworld
19
+ docker build --platform=linux/amd64 -t my-player:latest .
20
+ uv run coworld run-episode ./coworld/coworld_manifest.json my-player:latest
21
+ uv run coworld upload-policy my-player:latest --name my-player
22
+ uv run coworld submit my-player --league league_...
23
+ ```
24
+
25
+ If the image needs a specific player command:
26
+
27
+ ```bash
28
+ uv run coworld run-episode ./coworld/coworld_manifest.json my-runtime:latest --run python --run /app/player.py
29
+ uv run coworld upload-policy my-runtime:latest --name my-player --run python --run /app/player.py
30
+ ```
31
+
32
+ Check the submission and its first episodes:
33
+
34
+ ```bash
35
+ uv run coworld submissions --mine --league league_...
36
+ uv run coworld memberships --mine --division div_... --active-only
37
+ uv run coworld episodes --division div_... --mine --with-replay
38
+ ```
39
+
40
+ ## Coworld Packages
41
+
42
+ ```bash
43
+ uv run coworld certify path/to/coworld_manifest.json
44
+ uv run coworld upload-coworld path/to/coworld_manifest.json
45
+ uv run coworld list
46
+ uv run coworld show cow_...
47
+ uv run coworld images
48
+ uv run coworld images img_...
49
+ ```
50
+
51
+ ## Tournaments
52
+
53
+ Inspect the tournament structure:
54
+
55
+ ```bash
56
+ uv run coworld leagues
57
+ uv run coworld leagues league_...
58
+ uv run coworld divisions --league league_...
59
+ uv run coworld divisions div_...
60
+ uv run coworld rounds --division div_... --status completed
61
+ uv run coworld rounds round_...
62
+ uv run coworld pools --round round_...
63
+ uv run coworld pools pool_...
64
+ ```
65
+
66
+ Inspect tournament outcomes:
67
+
68
+ ```bash
69
+ uv run coworld results league_...
70
+ uv run coworld results div_...
71
+ uv run coworld results round_...
72
+ uv run coworld memberships --mine --division div_... --active-only
73
+ uv run coworld submissions --mine --league league_...
74
+ uv run coworld events --division div_...
75
+ ```
76
+
77
+ Most commands support `--json` for machine-readable output.
78
+
79
+ ## Episodes
80
+
81
+ List episode requests by pool, round, or division:
82
+
83
+ ```bash
84
+ uv run coworld episodes --division div_...
85
+ uv run coworld episodes --round round_... --with-replay
86
+ uv run coworld episodes --pool pool_... --policy my-policy:v3
87
+ uv run coworld episodes ereq_...
88
+ ```
89
+
90
+ Use `--mine` to keep only episodes involving policy versions in your current league memberships:
91
+
92
+ ```bash
93
+ uv run coworld episodes --division div_... --mine --with-replay --json
94
+ ```
95
+
96
+ Fetch artifacts for one episode request:
97
+
98
+ ```bash
99
+ uv run coworld episode-stats ereq_...
100
+ uv run coworld episode-stats ereq_... --json
101
+ uv run coworld episode-results ereq_... --output results.json
102
+ uv run coworld episode-logs ereq_... --list
103
+ uv run coworld episode-logs ereq_... --agent 0
104
+ uv run coworld episode-logs ereq_... --mine --download-dir logs/
105
+ ```
106
+
107
+ Download replay files:
108
+
109
+ ```bash
110
+ uv run coworld replays --division div_... --mine --download-dir replays/
111
+ uv run coworld replays --round round_... --policy my-policy:v3 --json
112
+ ```
113
+
114
+ `replays --download-dir` writes one replay JSON file per episode request plus `index.json` metadata with the episode,
115
+ job, Coworld, participant, score, and source replay URI details.
116
+
117
+ Open one replay:
118
+
119
+ ```bash
120
+ uv run coworld replay-open ereq_...
121
+ uv run coworld replay-open ereq_... --hosted
122
+ ```
123
+
124
+ The default `replay-open` path runs the replay locally with the Coworld manifest and replay artifact. `--hosted` creates
125
+ an Observatory-hosted replay session and prints the viewer URL.
@@ -0,0 +1,130 @@
1
+ # Coworld Game Runtime Contract
2
+
3
+ This is the canonical contract for game images. Player authors usually only need the player protocol linked from a
4
+ Coworld manifest; game authors need this file.
5
+
6
+ A Coworld episode has one game container and one player container per slot. The runner starts the game, gives it a
7
+ config, starts the players, records logs, and collects results and replay artifacts. The game owns the rules. Players
8
+ connect over websocket and choose actions.
9
+
10
+ ## Manifest Fields
11
+
12
+ The `game` object in `coworld_manifest.json` describes the game container:
13
+
14
+ - `name`, `version`, `description`, and `owner`
15
+ - `runnable`: Docker image, optional command, and public environment variables
16
+ - `config_schema`: JSON Schema for the runtime game config
17
+ - `results_schema`: JSON Schema for the final results file
18
+ - `protocols.player`: player websocket protocol docs
19
+ - `protocols.global`: live viewer websocket protocol docs
20
+
21
+ Protocol docs are document objects:
22
+
23
+ ```json
24
+ { "type": "uri", "value": "https://example.com/player_protocol.md" }
25
+ ```
26
+
27
+ Use `type: "uri"` for public HTTP(S) docs and `type: "text"` only for deliberately inline docs.
28
+
29
+ ## Player Slots
30
+
31
+ The game config schema must require a `tokens` field. It must be a string array with equal `minItems` and `maxItems`.
32
+ That fixed length is the number of player slots.
33
+
34
+ Coworld-authored configs, variants, and certification fixtures do not include `tokens`. The runner creates fresh tokens
35
+ for each episode and injects them into the concrete runtime config.
36
+
37
+ The game must reject a `/player` websocket connection when the slot/token pair is not valid for that episode.
38
+
39
+ ## Rollout Mode
40
+
41
+ For a normal episode, the runner starts the game with:
42
+
43
+ ```bash
44
+ COGAME_CONFIG_URI=...
45
+ COGAME_RESULTS_URI=...
46
+ COGAME_SAVE_REPLAY_URI=...
47
+ ```
48
+
49
+ The game must support `file://` URIs. Hosted runners may use other writable URI schemes when the game supports them.
50
+
51
+ In rollout mode, the game listens on `0.0.0.0:8080` and exposes:
52
+
53
+ - `GET /healthz`
54
+ - `GET /clients/player?slot=0&token=...&...`
55
+ - `WEBSOCKET /player?slot=0&token=...&...`
56
+ - `GET /clients/global`
57
+ - `WEBSOCKET /global`
58
+
59
+ `GET /healthz` returns 200 when the game is ready.
60
+
61
+ `GET /clients/player` serves a browser client for one slot. `GET /clients/global` serves a live viewer.
62
+
63
+ The served browser clients read the complete page query string before opening their websocket. If the query contains
64
+ `address`, the client uses that value as the full websocket URL after converting `http` or `https` to `ws` or `wss`;
65
+ it must not merge other page query params into that URL. Otherwise, the client derives the websocket URL by replacing
66
+ `/clients/player` with `/player`, or `/clients/global` with `/global`, and preserving the page query params such as
67
+ `slot`, `token`, and game-owned params.
68
+
69
+ For example, `/clients/player?slot=0&token=abc&role=scout` should open
70
+ `/player?slot=0&token=abc&role=scout`. A hosted proxy may instead serve
71
+ `/clients/player?address=wss://example.com/player?slot=0&token=abc`.
72
+
73
+ The `/global` websocket must support late viewers. A viewer that joins after the episode starts should receive enough
74
+ state to render from that point forward.
75
+
76
+ The `/player` websocket must allow the same slot to reconnect with the same token while the episode is still running.
77
+ The slot's game state survives disconnects. During a disconnect, the game may use no-op actions or another documented
78
+ behavior.
79
+
80
+ Games may expose local-only admin controls by convention:
81
+
82
+ - `GET /clients/admin`
83
+ - `WEBSOCKET /admin?...`
84
+
85
+ The admin route is game-owned. The platform must not expose `/admin` in production.
86
+
87
+ ## Replay Mode
88
+
89
+ For replay viewing, the runner starts or reuses the same game image with:
90
+
91
+ ```bash
92
+ COGAME_REPLAY_SERVER=1
93
+ ```
94
+
95
+ In replay mode, the game listens on `0.0.0.0:8080` and exposes:
96
+
97
+ - `GET /healthz`
98
+ - `GET /clients/replay?uri=<uri>`
99
+ - `WEBSOCKET /replay?uri=<uri>`
100
+
101
+ `GET /clients/replay?uri=<uri>` serves a browser replay viewer. The served client opens `/replay?uri=<uri>`. The game
102
+ loads the replay artifact and handles game-owned replay controls such as start, stop, seek, or speed changes.
103
+
104
+ The replay artifact format and replay websocket protocol are game-owned.
105
+
106
+ ## Episode Lifecycle
107
+
108
+ 1. The runner receives a job with the manifest, game config, players, and artifact output URIs.
109
+ 2. The runner generates one token per player slot.
110
+ 3. The runner writes the concrete game config, including `tokens`.
111
+ 4. The runner starts the game container with config, result, and replay URIs.
112
+ 5. The game reads its config and starts listening on port 8080.
113
+ 6. The runner waits for `GET /healthz` to return 200.
114
+ 7. The runner starts one player container per slot.
115
+ 8. Each player gets `COGAMES_ENGINE_WS_URL=ws://<engine-host>/player?slot=<slot>&token=<token>`.
116
+ 9. Players connect to `/player` and send game-specific actions.
117
+ 10. Viewers may connect to `/global`.
118
+ 11. The game runs until the episode ends.
119
+ 12. The game writes results to `COGAME_RESULTS_URI`.
120
+ 13. The game writes a replay to `COGAME_SAVE_REPLAY_URI`.
121
+ 14. The runner validates results against `results_schema` and stores results, replay, and logs.
122
+
123
+ The final results file must match `game.results_schema`. It must include `scores`, one number per player slot, and may
124
+ include extra game-specific fields declared by the schema.
125
+
126
+ ## Certification
127
+
128
+ Coworld certification turns the manifest's `certification` fixture into one local episode. It runs the game and bundled
129
+ player images, checks the HTTP routes, verifies that bad player tokens are rejected, validates final results, and checks
130
+ that the replay viewer can start.